v2 explicit destination traffic permissions (#18823)

* workload identity boilerplate

* notes from discussion with Iryna

* WIP traffic permissions controller poc

* workload identity, traffic permissions validation, errors, types

* traffic permissions mapper framing, traffic permissions controller updates.

* more roughing out of the controller

* cleanup

* controller and mapper logic

* tests

* refactor mapper logic, add tests

* clean up tenancy and integration test stubs

* consolidate mapping

* cleanup cache leak, revert bimapper changes

* address review comments

* test fix and rebase

* use resource helper

---------

Co-authored-by: John Landa <john.landa@hashicorp.com>
This commit is contained in:
skpratt 2023-09-25 11:50:07 -05:00 committed by GitHub
parent bd2fdb7f7d
commit 202090e5d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 1085 additions and 130 deletions

View File

@ -4,12 +4,38 @@
package auth
import (
"github.com/hashicorp/consul/internal/auth/internal/controllers"
"github.com/hashicorp/consul/internal/auth/internal/controllers/trafficpermissions"
"github.com/hashicorp/consul/internal/auth/internal/mappers/trafficpermissionsmapper"
"github.com/hashicorp/consul/internal/auth/internal/types"
"github.com/hashicorp/consul/internal/controller"
"github.com/hashicorp/consul/internal/resource"
)
var (
// Controller statuses
StatusKey = trafficpermissions.StatusKey
TrafficPermissionsConditionComputed = trafficpermissions.ConditionComputed
TrafficPermissionsConditionFailedToCompute = trafficpermissions.ConditionFailedToCompute
)
// RegisterTypes adds all resource types within the "catalog" API group
// to the given type registry
func RegisterTypes(r resource.Registry) {
types.Register(r)
}
type ControllerDependencies = controllers.Dependencies
func DefaultControllerDependencies() ControllerDependencies {
return ControllerDependencies{
TrafficPermissionsMapper: trafficpermissionsmapper.New(),
}
}
// RegisterControllers registers controllers for the auth types with
// the given controller manager.
func RegisterControllers(mgr *controller.Manager, deps ControllerDependencies) {
controllers.Register(mgr, deps)
}

View File

@ -0,0 +1,17 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package controllers
import (
"github.com/hashicorp/consul/internal/auth/internal/controllers/trafficpermissions"
"github.com/hashicorp/consul/internal/controller"
)
type Dependencies struct {
TrafficPermissionsMapper trafficpermissions.TrafficPermissionsMapper
}
func Register(mgr *controller.Manager, deps Dependencies) {
mgr.Register(trafficpermissions.Controller(deps.TrafficPermissionsMapper))
}

View File

@ -0,0 +1,189 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package trafficpermissions
import (
"context"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"
"github.com/hashicorp/consul/internal/controller"
"github.com/hashicorp/consul/internal/resource"
pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1"
"github.com/hashicorp/consul/proto-public/pbresource"
)
// TrafficPermissionsMapper is used to map a watch event for a TrafficPermissions resource and translate
// it to a ComputedTrafficPermissions resource which contains the effective permissions
// from all referencing TrafficPermissions resources.
type TrafficPermissionsMapper interface {
// MapTrafficPermissions will take a TrafficPermission resource and return controller requests for all
// ComputedTrafficPermissions associated with that TrafficPermission.
MapTrafficPermissions(context.Context, controller.Runtime, *pbresource.Resource) ([]controller.Request, error)
// UntrackTrafficPermissions instructs the Mapper to forget about the TrafficPermission.
UntrackTrafficPermissions(*pbresource.ID)
// GetTrafficPermissionsForCTP returns the tracked TrafficPermissions that are used to create a CTP
GetTrafficPermissionsForCTP(*pbresource.ID) []*pbresource.Reference
}
// Controller creates a controller for automatic ComputedTrafficPermissions management for
// updates to WorkloadIdentity or TrafficPermission resources.
func Controller(mapper TrafficPermissionsMapper) controller.Controller {
if mapper == nil {
panic("No TrafficPermissionsMapper was provided to the TrafficPermissionsController constructor")
}
return controller.ForType(pbauth.ComputedTrafficPermissionsType).
WithWatch(pbauth.WorkloadIdentityType, controller.ReplaceType(pbauth.ComputedTrafficPermissionsType)).
WithWatch(pbauth.TrafficPermissionsType, mapper.MapTrafficPermissions).
WithReconciler(&reconciler{mapper: mapper})
}
type reconciler struct {
mapper TrafficPermissionsMapper
}
// Reconcile will reconcile one ComputedTrafficPermission (CTP) in response to some event.
// Events include adding, modifying or deleting a WorkloadIdentity or TrafficPermission.
func (r *reconciler) Reconcile(ctx context.Context, rt controller.Runtime, req controller.Request) error {
rt.Logger = rt.Logger.With("resource-id", req.ID, "controller", StatusKey)
/*
* A CTP ID could come in for a variety of reasons.
* 1. workload identity create / delete: this results in the creation / deletion of a new CTP
* 2. traffic permission create / modify / delete: this results in a potential modification of an existing CTP
*
* Part 1: Handle Workload Identity changes:
* Check if the workload identity exists. If it doesn't we can stop here.
* CTPs are always generated from WorkloadIdentities, therefore the WI resource must already exist.
* If it is missing, that means it was deleted.
*/
ctpID := req.ID
wi := resource.ReplaceType(pbauth.WorkloadIdentityType, ctpID)
workloadIdentity, err := resource.GetDecodedResource[*pbauth.WorkloadIdentity](ctx, rt.Client, wi)
if err != nil {
rt.Logger.Error("error retrieving corresponding Workload Identity", "error", err)
return err
}
if workloadIdentity == nil || workloadIdentity.Resource == nil {
rt.Logger.Trace("workload identity has been deleted")
return nil
}
// Check if CTP exists:
oldCTPData, err := resource.GetDecodedResource[*pbauth.ComputedTrafficPermissions](ctx, rt.Client, ctpID)
if err != nil {
rt.Logger.Error("error retrieving computed permissions", "error", err)
return err
}
var oldResource *pbresource.Resource
var owner *pbresource.ID
if oldCTPData == nil {
// CTP does not yet exist, so we need to make a new one
rt.Logger.Trace("creating new computed traffic permissions for workload identity")
owner = workloadIdentity.Resource.Id
} else {
oldResource = oldCTPData.Resource
owner = oldCTPData.Resource.Owner
}
// Part 2: Recompute a CTP from TP create / modify / delete, or create a new CTP from existing TPs:
latestTrafficPermissions, err := computeNewTrafficPermissions(ctx, rt, r.mapper, ctpID, oldResource)
if err != nil {
rt.Logger.Error("error calculating computed permissions", "error", err)
return err
}
if oldCTPData != nil && proto.Equal(oldCTPData.Data, latestTrafficPermissions) {
// there are no changes to the computed traffic permissions, and we can return early
rt.Logger.Trace("no new computed traffic permissions")
return nil
}
newCTPData, err := anypb.New(latestTrafficPermissions)
if err != nil {
rt.Logger.Error("error marshalling latest traffic permissions", "error", err)
writeFailedStatus(ctx, rt, oldResource, nil, err.Error())
return err
}
rt.Logger.Trace("writing computed traffic permissions")
rsp, err := rt.Client.Write(ctx, &pbresource.WriteRequest{
Resource: &pbresource.Resource{
Id: req.ID,
Data: newCTPData,
Owner: owner,
},
})
if err != nil || rsp.Resource == nil {
rt.Logger.Error("error writing new computed traffic permissions", "error", err)
writeFailedStatus(ctx, rt, oldResource, nil, err.Error())
return err
} else {
rt.Logger.Trace("new computed traffic permissions were successfully written")
}
newStatus := &pbresource.Status{
ObservedGeneration: rsp.Resource.Generation,
Conditions: []*pbresource.Condition{
ConditionComputed(req.ID.Name),
},
}
_, err = rt.Client.WriteStatus(ctx, &pbresource.WriteStatusRequest{
Id: rsp.Resource.Id,
Key: StatusKey,
Status: newStatus,
})
return err
}
func writeFailedStatus(ctx context.Context, rt controller.Runtime, ctp *pbresource.Resource, tp *pbresource.ID, errDetail string) error {
if ctp == nil {
return nil
}
newStatus := &pbresource.Status{
ObservedGeneration: ctp.Generation,
Conditions: []*pbresource.Condition{
ConditionFailedToCompute(ctp.Id.Name, tp.Name, errDetail),
},
}
_, err := rt.Client.WriteStatus(ctx, &pbresource.WriteStatusRequest{
Id: ctp.Id,
Key: StatusKey,
Status: newStatus,
})
return err
}
// computeNewTrafficPermissions will use all associated Traffic Permissions to create new ComputedTrafficPermissions data
func computeNewTrafficPermissions(ctx context.Context, rt controller.Runtime, wm TrafficPermissionsMapper, ctpID *pbresource.ID, ctp *pbresource.Resource) (*pbauth.ComputedTrafficPermissions, error) {
// Part 1: Get all TPs that apply to workload identity
// Get already associated WorkloadIdentities/CTPs for reconcile requests:
trackedTPs := wm.GetTrafficPermissionsForCTP(ctpID)
if len(trackedTPs) > 0 {
rt.Logger.Trace("got tracked traffic permissions for CTP", "tps:", trackedTPs)
} else {
rt.Logger.Trace("found no tracked traffic permissions for CTP")
}
ap := make([]*pbauth.Permission, 0)
dp := make([]*pbauth.Permission, 0)
for _, t := range trackedTPs {
rsp, err := resource.GetDecodedResource[*pbauth.TrafficPermissions](ctx, rt.Client, resource.IDFromReference(t))
if err != nil {
rt.Logger.Error("error reading traffic permissions resource for computation", "error", err)
writeFailedStatus(ctx, rt, ctp, resource.IDFromReference(t), err.Error())
return nil, err
}
if rsp == nil {
rt.Logger.Trace("untracking deleted TrafficPermissions", "traffic-permissions-name", t.Name)
wm.UntrackTrafficPermissions(resource.IDFromReference(t))
continue
}
if rsp.Data.Action == pbauth.Action_ACTION_ALLOW {
ap = append(ap, rsp.Data.Permissions...)
} else {
dp = append(dp, rsp.Data.Permissions...)
}
}
return &pbauth.ComputedTrafficPermissions{AllowPermissions: ap, DenyPermissions: dp}, nil
}

View File

@ -0,0 +1,617 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package trafficpermissions
import (
"context"
"testing"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
"github.com/hashicorp/consul/internal/auth/internal/mappers/trafficpermissionsmapper"
"github.com/hashicorp/consul/internal/auth/internal/types"
"github.com/hashicorp/consul/internal/controller"
"github.com/hashicorp/consul/internal/resource"
rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1"
"github.com/hashicorp/consul/proto-public/pbresource"
"github.com/hashicorp/consul/proto/private/prototest"
"github.com/hashicorp/consul/sdk/testutil"
)
type controllerSuite struct {
suite.Suite
ctx context.Context
client *rtest.Client
rt controller.Runtime
mapper *trafficpermissionsmapper.TrafficPermissionsMapper
reconciler *reconciler
}
func (suite *controllerSuite) SetupTest() {
suite.ctx = testutil.TestContext(suite.T())
client := svctest.RunResourceService(suite.T(), types.Register)
suite.client = rtest.NewClient(client)
suite.rt = controller.Runtime{
Client: suite.client,
Logger: testutil.Logger(suite.T()),
}
suite.mapper = trafficpermissionsmapper.New()
suite.reconciler = &reconciler{mapper: suite.mapper}
}
func (suite *controllerSuite) requireTrafficPermissionsTracking(tp *pbresource.Resource, ids ...*pbresource.ID) {
reqs, err := suite.mapper.MapTrafficPermissions(suite.ctx, suite.rt, tp)
require.NoError(suite.T(), err)
require.Len(suite.T(), reqs, len(ids))
for _, id := range ids {
prototest.AssertContainsElement(suite.T(), reqs, controller.Request{ID: id})
}
for _, req := range reqs {
prototest.AssertContainsElement(suite.T(), ids, req.ID)
}
}
func (suite *controllerSuite) requireCTP(resource *pbresource.Resource, allowExpected []*pbauth.Permission, denyExpected []*pbauth.Permission) {
var ctp pbauth.ComputedTrafficPermissions
require.NoError(suite.T(), resource.Data.UnmarshalTo(&ctp))
require.Len(suite.T(), ctp.AllowPermissions, len(allowExpected))
require.Len(suite.T(), ctp.DenyPermissions, len(denyExpected))
prototest.AssertElementsMatch(suite.T(), allowExpected, ctp.AllowPermissions)
prototest.AssertElementsMatch(suite.T(), denyExpected, ctp.DenyPermissions)
}
func (suite *controllerSuite) TestReconcile_CTPCreate_NoReferencingTrafficPermissionsExist() {
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").Write(suite.T(), suite.client)
require.NotNil(suite.T(), wi)
id := rtest.Resource(pbauth.ComputedTrafficPermissionsType, wi.Id.Name).WithTenancy(resource.DefaultNamespacedTenancy()).WithOwner(wi.Id).ID()
require.NotNil(suite.T(), id)
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
require.NoError(suite.T(), err)
// Ensure that the CTP was created
ctp := suite.client.RequireResourceExists(suite.T(), id)
suite.requireCTP(ctp, []*pbauth.Permission{}, []*pbauth.Permission{})
}
func (suite *controllerSuite) TestReconcile_CTPCreate_ReferencingTrafficPermissionsExist() {
// create dead-end traffic permissions
p1 := &pbauth.Permission{}
tp1 := rtest.Resource(pbauth.TrafficPermissionsType, "tp1").WithData(suite.T(), &pbauth.TrafficPermissions{
Destination: &pbauth.Destination{
IdentityName: "wi1",
},
Action: pbauth.Action_ACTION_DENY,
Permissions: []*pbauth.Permission{p1},
}).Write(suite.T(), suite.client)
wi1ID := &pbresource.ID{
Name: "wi1",
Type: pbauth.ComputedTrafficPermissionsType,
Tenancy: tp1.Id.Tenancy,
}
suite.requireTrafficPermissionsTracking(tp1, wi1ID)
p2 := &pbauth.Permission{
Sources: []*pbauth.Source{
{
IdentityName: "wi2",
Namespace: "default",
Partition: "default",
Peer: "local",
}},
}
tp2 := rtest.Resource(pbauth.TrafficPermissionsType, "tp2").WithData(suite.T(), &pbauth.TrafficPermissions{
Destination: &pbauth.Destination{
IdentityName: "wi1",
},
Action: pbauth.Action_ACTION_ALLOW,
Permissions: []*pbauth.Permission{p2},
}).Write(suite.T(), suite.client)
suite.requireTrafficPermissionsTracking(tp2, wi1ID)
// create the workload identity that they reference
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").Write(suite.T(), suite.client)
id := rtest.Resource(pbauth.ComputedTrafficPermissionsType, wi.Id.Name).WithTenancy(resource.DefaultNamespacedTenancy()).WithOwner(wi.Id).ID()
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
require.NoError(suite.T(), err)
// Ensure that the CTP was created
ctp := suite.client.RequireResourceExists(suite.T(), id)
suite.requireCTP(ctp, []*pbauth.Permission{p2}, []*pbauth.Permission{p1})
rtest.RequireOwner(suite.T(), ctp, wi.Id, true)
}
func (suite *controllerSuite) TestReconcile_WorkloadIdentityDelete_ReferencingTrafficPermissionsExist() {
p1 := &pbauth.Permission{}
tp1 := rtest.Resource(pbauth.TrafficPermissionsType, "tp1").WithData(suite.T(), &pbauth.TrafficPermissions{
Destination: &pbauth.Destination{
IdentityName: "wi1",
},
Action: pbauth.Action_ACTION_DENY,
Permissions: []*pbauth.Permission{p1},
}).Write(suite.T(), suite.client)
wi1ID := &pbresource.ID{
Name: "wi1",
Type: pbauth.ComputedTrafficPermissionsType,
Tenancy: tp1.Id.Tenancy,
}
suite.requireTrafficPermissionsTracking(tp1, wi1ID)
p2 := &pbauth.Permission{
Sources: []*pbauth.Source{
{
IdentityName: "wi2",
Namespace: "default",
Partition: "default",
Peer: "local",
}},
}
tp2 := rtest.Resource(pbauth.TrafficPermissionsType, "tp2").WithData(suite.T(), &pbauth.TrafficPermissions{
Destination: &pbauth.Destination{
IdentityName: "wi1",
},
Action: pbauth.Action_ACTION_ALLOW,
Permissions: []*pbauth.Permission{p2},
}).Write(suite.T(), suite.client)
suite.requireTrafficPermissionsTracking(tp2, wi1ID)
// create the workload identity that they reference
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").Write(suite.T(), suite.client)
id := rtest.Resource(pbauth.ComputedTrafficPermissionsType, wi.Id.Name).WithTenancy(resource.DefaultNamespacedTenancy()).WithOwner(wi.Id).ID()
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
require.NoError(suite.T(), err)
// delete the workload identity
suite.client.MustDelete(suite.T(), wi.Id)
// re-reconcile: should untrack the CTP
err = suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
require.NoError(suite.T(), err)
}
func (suite *controllerSuite) TestReconcile_WorkloadIdentityDelete_NoReferencingTrafficPermissionsExist() {
// create the workload identity that they reference
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").Write(suite.T(), suite.client)
id := rtest.Resource(pbauth.ComputedTrafficPermissionsType, wi.Id.Name).WithTenancy(resource.DefaultNamespacedTenancy()).WithOwner(wi.Id).ID()
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
require.NoError(suite.T(), err)
// delete the workload identity
suite.client.MustDelete(suite.T(), wi.Id)
// re-reconcile: should untrack the CTP
err = suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
require.NoError(suite.T(), err)
// there should not be any traffic permissions to compute
tps := suite.mapper.GetTrafficPermissionsForCTP(id)
require.Len(suite.T(), tps, 0)
}
func (suite *controllerSuite) TestReconcile_TrafficPermissionsCreate_DestinationWorkloadIdentityExists() {
// create the workload identity to be referenced
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").Write(suite.T(), suite.client)
id := rtest.Resource(pbauth.ComputedTrafficPermissionsType, wi.Id.Name).WithTenancy(resource.DefaultNamespacedTenancy()).WithOwner(wi.Id).ID()
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
require.NoError(suite.T(), err)
// create traffic permissions
p1 := &pbauth.Permission{}
tp1 := rtest.Resource(pbauth.TrafficPermissionsType, "tp1").WithData(suite.T(), &pbauth.TrafficPermissions{
Destination: &pbauth.Destination{
IdentityName: "wi1",
},
Action: pbauth.Action_ACTION_DENY,
Permissions: []*pbauth.Permission{p1},
}).Write(suite.T(), suite.client)
suite.requireTrafficPermissionsTracking(tp1, id)
p2 := &pbauth.Permission{
Sources: []*pbauth.Source{
{
IdentityName: "wi2",
Namespace: "default",
Partition: "default",
Peer: "local",
}},
}
tp2 := rtest.Resource(pbauth.TrafficPermissionsType, "tp2").WithData(suite.T(), &pbauth.TrafficPermissions{
Destination: &pbauth.Destination{
IdentityName: "wi1",
},
Action: pbauth.Action_ACTION_ALLOW,
Permissions: []*pbauth.Permission{p2},
}).Write(suite.T(), suite.client)
suite.requireTrafficPermissionsTracking(tp2, id)
err = suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
require.NoError(suite.T(), err)
// Ensure that the CTP was updated
ctp := suite.client.RequireResourceExists(suite.T(), id)
suite.requireCTP(ctp, []*pbauth.Permission{p2}, []*pbauth.Permission{p1})
rtest.RequireOwner(suite.T(), ctp, wi.Id, true)
// Add another TP
p3 := &pbauth.Permission{
Sources: []*pbauth.Source{
{
IdentityName: "wi3",
Namespace: "default",
Partition: "default",
Peer: "local",
}},
}
tp3 := rtest.Resource(pbauth.TrafficPermissionsType, "tp3").WithData(suite.T(), &pbauth.TrafficPermissions{
Destination: &pbauth.Destination{
IdentityName: "wi1",
},
Action: pbauth.Action_ACTION_DENY,
Permissions: []*pbauth.Permission{p3},
}).Write(suite.T(), suite.client)
suite.requireTrafficPermissionsTracking(tp3, id)
err = suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
require.NoError(suite.T(), err)
// Ensure that the CTP was updated
ctp = suite.client.RequireResourceExists(suite.T(), id)
suite.requireCTP(ctp, []*pbauth.Permission{p2}, []*pbauth.Permission{p1, p3})
rtest.RequireOwner(suite.T(), ctp, wi.Id, true)
}
func (suite *controllerSuite) TestReconcile_TrafficPermissionsDelete_DestinationWorkloadIdentityExists() {
// create the workload identity to be referenced
wi := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").Write(suite.T(), suite.client)
id := rtest.Resource(pbauth.ComputedTrafficPermissionsType, wi.Id.Name).WithTenancy(resource.DefaultNamespacedTenancy()).WithOwner(wi.Id).ID()
err := suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
require.NoError(suite.T(), err)
// create traffic permissions
p1 := &pbauth.Permission{}
tp1 := rtest.Resource(pbauth.TrafficPermissionsType, "tp1").WithData(suite.T(), &pbauth.TrafficPermissions{
Destination: &pbauth.Destination{
IdentityName: "wi1",
},
Action: pbauth.Action_ACTION_DENY,
Permissions: []*pbauth.Permission{p1},
}).Write(suite.T(), suite.client)
suite.requireTrafficPermissionsTracking(tp1, id)
p2 := &pbauth.Permission{
Sources: []*pbauth.Source{
{
IdentityName: "wi2",
Namespace: "default",
Partition: "default",
Peer: "local",
}},
}
tp2 := rtest.Resource(pbauth.TrafficPermissionsType, "tp2").WithData(suite.T(), &pbauth.TrafficPermissions{
Destination: &pbauth.Destination{
IdentityName: "wi1",
},
Action: pbauth.Action_ACTION_ALLOW,
Permissions: []*pbauth.Permission{p2},
}).Write(suite.T(), suite.client)
suite.requireTrafficPermissionsTracking(tp2, id)
err = suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
require.NoError(suite.T(), err)
ctp := suite.client.RequireResourceExists(suite.T(), id)
suite.requireCTP(ctp, []*pbauth.Permission{p2}, []*pbauth.Permission{p1})
rtest.RequireOwner(suite.T(), ctp, wi.Id, true)
// Delete TP2
suite.client.MustDelete(suite.T(), tp2.Id)
err = suite.reconciler.Reconcile(suite.ctx, suite.rt, controller.Request{ID: id})
require.NoError(suite.T(), err)
// Ensure that the CTP was updated
ctp = suite.client.RequireResourceExists(suite.T(), id)
suite.requireCTP(ctp, []*pbauth.Permission{}, []*pbauth.Permission{p1})
// Ensure TP2 is untracked
newTps := suite.mapper.GetTrafficPermissionsForCTP(ctp.Id)
require.Len(suite.T(), newTps, 1)
require.Equal(suite.T(), newTps[0].Name, tp1.Id.Name)
}
func (suite *controllerSuite) TestReconcile_TrafficPermissionsDelete_DestinationWorkloadIdentityDoesNotExist() {
// create traffic permissions
p1 := &pbauth.Permission{}
tp1 := rtest.Resource(pbauth.TrafficPermissionsType, "tp1").WithData(suite.T(), &pbauth.TrafficPermissions{
Destination: &pbauth.Destination{
IdentityName: "wi1",
},
Action: pbauth.Action_ACTION_DENY,
Permissions: []*pbauth.Permission{p1},
}).Write(suite.T(), suite.client)
wi1ID := &pbresource.ID{
Name: "wi1",
Type: pbauth.ComputedTrafficPermissionsType,
Tenancy: tp1.Id.Tenancy,
}
suite.requireTrafficPermissionsTracking(tp1, wi1ID)
p2 := &pbauth.Permission{
Sources: []*pbauth.Source{
{
IdentityName: "wi2",
Namespace: "default",
Partition: "default",
Peer: "local",
}},
}
tp2 := rtest.Resource(pbauth.TrafficPermissionsType, "tp2").WithData(suite.T(), &pbauth.TrafficPermissions{
Destination: &pbauth.Destination{
IdentityName: "wi1",
},
Action: pbauth.Action_ACTION_ALLOW,
Permissions: []*pbauth.Permission{p2},
}).Write(suite.T(), suite.client)
suite.requireTrafficPermissionsTracking(tp2, wi1ID)
// Delete TP2
suite.client.MustDelete(suite.T(), tp2.Id)
// Ensure that no CTPs exist
rsp, err := suite.client.List(suite.ctx, &pbresource.ListRequest{
Type: pbauth.ComputedTrafficPermissionsType,
Tenancy: resource.DefaultNamespacedTenancy(),
})
require.NoError(suite.T(), err)
require.Empty(suite.T(), rsp.Resources)
}
func (suite *controllerSuite) TestControllerBasic() {
// TODO: refactor this
// In this test we check basic operations for a workload identity and referencing traffic permission
mgr := controller.NewManager(suite.client, suite.rt.Logger)
mgr.Register(Controller(suite.mapper))
mgr.SetRaftLeader(true)
go mgr.Run(suite.ctx)
// Add a workload identity
workloadIdentity := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").Write(suite.T(), suite.client)
// Wait for the controller to record that the CTP has been computed
res := suite.client.WaitForReconciliation(suite.T(), resource.ReplaceType(pbauth.ComputedTrafficPermissionsType, workloadIdentity.Id), StatusKey)
// Check that the status was updated
rtest.RequireStatusCondition(suite.T(), res, StatusKey, ConditionComputed("wi1"))
// Check that the CTP resource exists and contains no permissions
ctpID := rtest.Resource(pbauth.ComputedTrafficPermissionsType, "wi1").ID()
ctpObject := suite.client.RequireResourceExists(suite.T(), ctpID)
suite.requireCTP(ctpObject, nil, nil)
// add a traffic permission that references wi1
p1 := &pbauth.Permission{
Sources: []*pbauth.Source{{
IdentityName: "wi2",
Namespace: "default",
Partition: "default",
Peer: "local",
}},
DestinationRules: nil,
}
tp1 := rtest.Resource(pbauth.TrafficPermissionsType, "tp1").WithData(suite.T(), &pbauth.TrafficPermissions{
Destination: &pbauth.Destination{IdentityName: "wi1"},
Action: pbauth.Action_ACTION_ALLOW,
Permissions: []*pbauth.Permission{p1},
}).Write(suite.T(), suite.client)
suite.client.RequireResourceExists(suite.T(), tp1.Id)
// Wait for the controller to record that the CTP has been re-computed
res = suite.client.WaitForReconciliation(suite.T(), resource.ReplaceType(pbauth.ComputedTrafficPermissionsType, workloadIdentity.Id), StatusKey)
rtest.RequireStatusCondition(suite.T(), res, StatusKey, ConditionComputed("wi1"))
// Check that the ctp has been regenerated
ctpObject = suite.client.WaitForNewVersion(suite.T(), ctpID, ctpObject.Version)
// check wi1
suite.requireCTP(ctpObject, []*pbauth.Permission{p1}, nil)
// add a traffic permission that references wi2
p2 := &pbauth.Permission{
Sources: []*pbauth.Source{{
IdentityName: "wi1",
Namespace: "default",
Partition: "default",
Peer: "local",
}},
DestinationRules: nil,
}
tp2 := rtest.Resource(pbauth.TrafficPermissionsType, "tp2").WithData(suite.T(), &pbauth.TrafficPermissions{
Destination: &pbauth.Destination{IdentityName: "wi2"},
Action: pbauth.Action_ACTION_ALLOW,
Permissions: []*pbauth.Permission{p2},
}).Write(suite.T(), suite.client)
suite.client.RequireResourceExists(suite.T(), tp2.Id)
// check wi1 is the same
ctpObject = suite.client.RequireResourceExists(suite.T(), ctpID)
suite.requireCTP(ctpObject, []*pbauth.Permission{p1}, nil)
// check no ctp2
ctpID2 := rtest.Resource(pbauth.ComputedTrafficPermissionsType, "wi2").ID()
suite.client.RequireResourceNotFound(suite.T(), ctpID2)
// delete tp1
suite.client.MustDelete(suite.T(), tp1.Id)
suite.client.WaitForDeletion(suite.T(), tp1.Id)
// check wi1 has no permissions
ctpObject = suite.client.WaitForNewVersion(suite.T(), ctpID, ctpObject.Version)
suite.requireCTP(ctpObject, nil, nil)
// edit tp2 to point to wi1
rtest.Resource(pbauth.TrafficPermissionsType, "tp2").WithData(suite.T(), &pbauth.TrafficPermissions{
Destination: &pbauth.Destination{IdentityName: "wi1"},
Action: pbauth.Action_ACTION_ALLOW,
Permissions: []*pbauth.Permission{p2},
}).Write(suite.T(), suite.client)
// check wi1 has tp2's permissions
ctpObject = suite.client.WaitForNewVersion(suite.T(), ctpID, ctpObject.Version)
suite.requireCTP(ctpObject, []*pbauth.Permission{p2}, nil)
// check no ctp2
ctpID2 = rtest.Resource(pbauth.ComputedTrafficPermissionsType, "wi2").ID()
suite.client.RequireResourceNotFound(suite.T(), ctpID2)
}
func (suite *controllerSuite) TestControllerMultipleTrafficPermissions() {
// TODO: refactor this, turn back on once timing flakes are understood
suite.T().Skip("flaky behavior observed")
// In this test we check operations for a workload identity and multiple referencing traffic permissions
mgr := controller.NewManager(suite.client, suite.rt.Logger)
mgr.Register(Controller(suite.mapper))
mgr.SetRaftLeader(true)
go mgr.Run(suite.ctx)
wi1ID := &pbresource.ID{
Name: "wi1",
Type: pbauth.ComputedTrafficPermissionsType,
Tenancy: resource.DefaultNamespacedTenancy(),
}
// add tp1 and tp2
p1 := &pbauth.Permission{
Sources: []*pbauth.Source{{
IdentityName: "wi2",
Namespace: "default",
Partition: "default",
Peer: "local",
}},
DestinationRules: nil,
}
tp1 := rtest.Resource(pbauth.TrafficPermissionsType, "tp1").WithData(suite.T(), &pbauth.TrafficPermissions{
Destination: &pbauth.Destination{IdentityName: "wi1"},
Action: pbauth.Action_ACTION_ALLOW,
Permissions: []*pbauth.Permission{p1},
}).Write(suite.T(), suite.client)
suite.client.RequireResourceExists(suite.T(), tp1.Id)
suite.requireTrafficPermissionsTracking(tp1, wi1ID)
p2 := &pbauth.Permission{
Sources: []*pbauth.Source{{
IdentityName: "wi3",
Namespace: "default",
Partition: "default",
Peer: "local",
}},
DestinationRules: nil,
}
tp2 := rtest.Resource(pbauth.TrafficPermissionsType, "tp2").WithData(suite.T(), &pbauth.TrafficPermissions{
Destination: &pbauth.Destination{IdentityName: "wi1"},
Action: pbauth.Action_ACTION_ALLOW,
Permissions: []*pbauth.Permission{p2},
}).Write(suite.T(), suite.client)
suite.client.RequireResourceExists(suite.T(), tp2.Id)
suite.requireTrafficPermissionsTracking(tp1, wi1ID)
// Add a workload identity
workloadIdentity := rtest.Resource(pbauth.WorkloadIdentityType, "wi1").Write(suite.T(), suite.client)
ctpID := resource.ReplaceType(pbauth.ComputedTrafficPermissionsType, workloadIdentity.Id)
// Wait for the controller to record that the CTP has been computed
res := suite.client.WaitForReconciliation(suite.T(), ctpID, StatusKey)
rtest.RequireStatusCondition(suite.T(), res, StatusKey, ConditionComputed("wi1"))
// check ctp1 has tp1 and tp2
ctpObject := suite.client.RequireResourceExists(suite.T(), res.Id)
suite.requireCTP(ctpObject, []*pbauth.Permission{p1, p2}, nil)
// add tp3
p3 := &pbauth.Permission{
Sources: []*pbauth.Source{{
IdentityName: "wi4",
Namespace: "default",
Partition: "default",
Peer: "local",
}},
DestinationRules: nil,
}
tp3 := rtest.Resource(pbauth.TrafficPermissionsType, "tp3").WithData(suite.T(), &pbauth.TrafficPermissions{
Destination: &pbauth.Destination{IdentityName: "wi1"},
Action: pbauth.Action_ACTION_DENY,
Permissions: []*pbauth.Permission{p3},
}).Write(suite.T(), suite.client)
suite.client.RequireResourceExists(suite.T(), tp3.Id)
// check ctp1 has tp3
ctpObject = suite.client.WaitForReconciliation(suite.T(), ctpObject.Id, StatusKey)
ctpObject = suite.client.WaitForNewVersion(suite.T(), ctpObject.Id, ctpObject.Version)
suite.requireCTP(ctpObject, []*pbauth.Permission{p1, p2}, []*pbauth.Permission{p3})
// delete ctp
suite.client.MustDelete(suite.T(), ctpObject.Id)
suite.client.WaitForDeletion(suite.T(), ctpObject.Id)
// check ctp regenerated, has all permissions
res = suite.client.WaitForReconciliation(suite.T(), ctpID, StatusKey)
rtest.RequireStatusCondition(suite.T(), res, StatusKey, ConditionComputed("wi1"))
ctpObject = suite.client.RequireResourceExists(suite.T(), res.Id)
suite.requireCTP(ctpObject, []*pbauth.Permission{p1, p2}, []*pbauth.Permission{p3})
// delete wi1
suite.client.MustDelete(suite.T(), workloadIdentity.Id)
suite.client.WaitForDeletion(suite.T(), workloadIdentity.Id)
// recreate wi1
rtest.Resource(pbauth.WorkloadIdentityType, "wi1").Write(suite.T(), suite.client)
// check ctp regenerated, has all permissions
res = suite.client.WaitForReconciliation(suite.T(), ctpID, StatusKey)
rtest.RequireStatusCondition(suite.T(), res, StatusKey, ConditionComputed("wi1"))
ctpObject = suite.client.RequireResourceExists(suite.T(), res.Id)
suite.requireCTP(ctpObject, []*pbauth.Permission{p1, p2}, []*pbauth.Permission{p3})
// delete tp3
suite.client.MustDelete(suite.T(), tp3.Id)
suite.client.WaitForDeletion(suite.T(), tp3.Id)
suite.client.RequireResourceNotFound(suite.T(), tp3.Id)
// check ctp1 has tp1 and tp2, and not tp3
res = suite.client.WaitForReconciliation(suite.T(), ctpObject.Id, StatusKey)
ctpObject = suite.client.WaitForNewVersion(suite.T(), res.Id, ctpObject.Version)
suite.requireCTP(ctpObject, []*pbauth.Permission{p1, p2}, nil)
// add wi2
workloadIdentity2 := rtest.Resource(pbauth.WorkloadIdentityType, "wi2").Write(suite.T(), suite.client)
// Wait for the controller to record that the CTP has been computed
res2 := suite.client.WaitForReconciliation(suite.T(), resource.ReplaceType(pbauth.ComputedTrafficPermissionsType, workloadIdentity2.Id), StatusKey)
rtest.RequireStatusCondition(suite.T(), res2, StatusKey, ConditionComputed("wi2"))
// check ctp2 has no permissions
ctpObject2 := suite.client.RequireResourceExists(suite.T(), res2.Id)
suite.requireCTP(ctpObject2, nil, nil)
// edit all traffic permissions to point to wi2
tp1 = rtest.Resource(pbauth.TrafficPermissionsType, "tp1").WithData(suite.T(), &pbauth.TrafficPermissions{
Destination: &pbauth.Destination{IdentityName: "wi2"},
Action: pbauth.Action_ACTION_ALLOW,
Permissions: []*pbauth.Permission{p1},
}).Write(suite.T(), suite.client)
tp2 = rtest.Resource(pbauth.TrafficPermissionsType, "tp2").WithData(suite.T(), &pbauth.TrafficPermissions{
Destination: &pbauth.Destination{IdentityName: "wi2"},
Action: pbauth.Action_ACTION_ALLOW,
Permissions: []*pbauth.Permission{p2},
}).Write(suite.T(), suite.client)
tp3 = rtest.Resource(pbauth.TrafficPermissionsType, "tp3").WithData(suite.T(), &pbauth.TrafficPermissions{
Destination: &pbauth.Destination{IdentityName: "wi2"},
Action: pbauth.Action_ACTION_DENY,
Permissions: []*pbauth.Permission{p3},
}).Write(suite.T(), suite.client)
suite.client.RequireResourceExists(suite.T(), tp1.Id)
suite.client.RequireResourceExists(suite.T(), tp2.Id)
suite.client.RequireResourceExists(suite.T(), tp3.Id)
// check wi2 has updated with all permissions after 6 reconciles
ctpObject2 = suite.client.WaitForReconciliation(suite.T(), ctpObject2.Id, StatusKey)
res2 = suite.client.WaitForReconciliation(suite.T(), ctpObject2.Id, StatusKey)
suite.client.WaitForResourceState(suite.T(), res2.Id, func(t rtest.T, res *pbresource.Resource) {
suite.requireCTP(res, []*pbauth.Permission{p1, p2}, []*pbauth.Permission{p3})
})
// check wi1 has no permissions after 6 reconciles
ctpObject = suite.client.WaitForReconciliation(suite.T(), ctpObject.Id, StatusKey)
res = suite.client.WaitForReconciliation(suite.T(), ctpObject.Id, StatusKey)
suite.client.WaitForResourceState(suite.T(), res.Id, func(t rtest.T, res *pbresource.Resource) {
suite.requireCTP(res, nil, nil)
})
}
func TestController(t *testing.T) {
suite.Run(t, new(controllerSuite))
}

View File

@ -0,0 +1,42 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package trafficpermissions
import (
"fmt"
"github.com/hashicorp/consul/proto-public/pbresource"
)
const (
StatusKey = "consul.io/traffic-permissions"
StatusTrafficPermissionsComputed = "Traffic permissions have been computed"
StatusTrafficPermissionsNotComputed = "Traffic permissions have been computed"
ConditionPermissionsAppliedMsg = "Workload identity %s has new permission set"
ConditionPermissionsFailedMsg = "Unable to calculate new permission set for Workload identity %s"
)
var (
ConditionComputed = func(workloadIdentity string) *pbresource.Condition {
return &pbresource.Condition{
Type: StatusTrafficPermissionsComputed,
State: pbresource.Condition_STATE_TRUE,
Message: fmt.Sprintf(ConditionPermissionsAppliedMsg, workloadIdentity),
}
}
ConditionFailedToCompute = func(workloadIdentity string, trafficPermissions string, errDetail string) *pbresource.Condition {
message := fmt.Sprintf(ConditionPermissionsFailedMsg, workloadIdentity)
if len(trafficPermissions) > 0 {
message = message + fmt.Sprintf(", traffic permission %s cannot be computed", trafficPermissions)
}
if len(errDetail) > 0 {
message = message + fmt.Sprintf(", error details: %s", errDetail)
}
return &pbresource.Condition{
Type: StatusTrafficPermissionsNotComputed,
State: pbresource.Condition_STATE_FALSE,
Message: message,
}
}
)

View File

@ -0,0 +1,73 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package trafficpermissionsmapper
import (
"context"
"sync"
"github.com/hashicorp/consul/internal/auth/internal/types"
"github.com/hashicorp/consul/internal/controller"
"github.com/hashicorp/consul/internal/resource"
"github.com/hashicorp/consul/internal/resource/mappers/bimapper"
pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1"
"github.com/hashicorp/consul/proto-public/pbresource"
)
type TrafficPermissionsMapper struct {
lock sync.Mutex
mapper *bimapper.Mapper
}
func New() *TrafficPermissionsMapper {
return &TrafficPermissionsMapper{
lock: sync.Mutex{},
mapper: bimapper.New(pbauth.TrafficPermissionsType, pbauth.ComputedTrafficPermissionsType),
}
}
// TrafficPermissionsMapper functions
func (tm *TrafficPermissionsMapper) MapTrafficPermissions(_ context.Context, _ controller.Runtime, res *pbresource.Resource) ([]controller.Request, error) {
tm.lock.Lock()
defer tm.lock.Unlock()
var tp pbauth.TrafficPermissions
err := res.Data.UnmarshalTo(&tp)
if err != nil {
return nil, resource.NewErrDataParse(&tp, err)
}
// get new CTP associations based on destination
if len(tp.Destination.IdentityName) == 0 {
// this error should never happen if validation is working
return nil, types.ErrWildcardNotSupported
}
newCTP := &pbresource.ID{
Name: tp.Destination.IdentityName,
Type: pbauth.ComputedTrafficPermissionsType,
Tenancy: res.Id.Tenancy,
}
requests := []controller.Request{{ID: newCTP}}
// add already associated WorkloadIdentities/CTPs for reconcile requests:
oldCTPs := tm.mapper.LinkIDsForItem(res.Id)
for _, mappedWI := range oldCTPs {
if mappedWI.Name != newCTP.Name {
requests = append(requests, controller.Request{ID: mappedWI})
}
}
// re-map traffic permission to new CTP
tm.mapper.TrackItem(res.Id, []resource.ReferenceOrID{newCTP})
return requests, nil
}
func (tm *TrafficPermissionsMapper) UntrackTrafficPermissions(tp *pbresource.ID) {
tm.lock.Lock()
defer tm.lock.Unlock()
tm.mapper.UntrackItem(tp)
}
func (tm *TrafficPermissionsMapper) GetTrafficPermissionsForCTP(ctp *pbresource.ID) []*pbresource.Reference {
tm.lock.Lock()
defer tm.lock.Unlock()
return tm.mapper.ItemRefsForLink(ctp)
}

View File

@ -11,4 +11,5 @@ var (
errSourceWildcards = errors.New("permission sources may not have wildcard namespaces and explicit names.")
errSourceExcludes = errors.New("must be defined on wildcard sources")
errInvalidPrefixValues = errors.New("prefix values, regex values, and explicit names must not combined")
ErrWildcardNotSupported = errors.New("traffic permissions without explicit destinations are not yet supported")
)

View File

@ -44,10 +44,12 @@ func (d *DecodedResource[T]) GetData() T {
func Decode[T proto.Message](res *pbresource.Resource) (*DecodedResource[T], error) {
var zero T
data := zero.ProtoReflect().New().Interface().(T)
// check that there is data to unmarshall
if res.Data != nil {
if err := res.Data.UnmarshalTo(data); err != nil {
return nil, NewErrDataParse(data, err)
}
}
return &DecodedResource[T]{
Resource: res,
Data: data,
@ -64,6 +66,5 @@ func GetDecodedResource[T proto.Message](ctx context.Context, client pbresource.
case err != nil:
return nil, err
}
return Decode[T](rsp.Resource)
}

View File

@ -269,7 +269,6 @@ type Destination struct {
unknownFields protoimpl.UnknownFields
IdentityName string `protobuf:"bytes,1,opt,name=identity_name,json=identityName,proto3" json:"identity_name,omitempty"`
IdentityPrefix string `protobuf:"bytes,2,opt,name=identity_prefix,json=identityPrefix,proto3" json:"identity_prefix,omitempty"`
}
func (x *Destination) Reset() {
@ -311,13 +310,6 @@ func (x *Destination) GetIdentityName() string {
return ""
}
func (x *Destination) GetIdentityPrefix() string {
if x != nil {
return x.IdentityPrefix
}
return ""
}
// permissions is a list of permissions to match on.
type Permission struct {
state protoimpl.MessageState
@ -881,120 +873,118 @@ var file_pbauth_v2beta1_traffic_permissions_proto_rawDesc = []byte{
0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x61, 0x75, 0x74, 0x68,
0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73,
0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73,
0x3a, 0x06, 0xa2, 0x93, 0x04, 0x02, 0x08, 0x02, 0x22, 0x5b, 0x0a, 0x0b, 0x44, 0x65, 0x73, 0x74,
0x3a, 0x06, 0xa2, 0x93, 0x04, 0x02, 0x08, 0x02, 0x22, 0x32, 0x0a, 0x0b, 0x44, 0x65, 0x73, 0x74,
0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x64, 0x65, 0x6e, 0x74,
0x69, 0x74, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f,
0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x50,
0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0xaa, 0x01, 0x0a, 0x0a, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73,
0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18,
0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72,
0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x76, 0x32,
0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x07, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x5b, 0x0a, 0x11, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x2e, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e,
0x73, 0x75, 0x6c, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31,
0x2e, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65,
0x52, 0x10, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c,
0x65, 0x73, 0x22, 0xec, 0x01, 0x0a, 0x06, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x23, 0x0a,
0x0d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4e, 0x61,
0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65,
0x12, 0x1c, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20,
0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12,
0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x65,
0x65, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x61, 0x6d, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x5f, 0x67,
0x72, 0x6f, 0x75, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x61, 0x6d, 0x65,
0x6e, 0x65, 0x73, 0x73, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x46, 0x0a, 0x07, 0x65, 0x78, 0x63,
0x6c, 0x75, 0x64, 0x65, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x68, 0x61, 0x73,
0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x61, 0x75,
0x74, 0x68, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x63, 0x6c, 0x75,
0x64, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x07, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64,
0x65, 0x22, 0xab, 0x01, 0x0a, 0x0d, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x53, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f,
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x64, 0x65, 0x6e,
0x74, 0x69, 0x74, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65,
0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d,
0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74,
0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x72, 0x74, 0x69,
0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01,
0x28, 0x09, 0x52, 0x04, 0x70, 0x65, 0x65, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x61, 0x6d, 0x65,
0x6e, 0x65, 0x73, 0x73, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
0x52, 0x0d, 0x73, 0x61, 0x6d, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x22,
0xc7, 0x02, 0x0a, 0x0f, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52,
0x75, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x65, 0x78, 0x61, 0x63,
0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x74, 0x68, 0x45, 0x78, 0x61,
0x63, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69,
0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x61, 0x74, 0x68, 0x50, 0x72, 0x65,
0x66, 0x69, 0x78, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x72, 0x65, 0x67, 0x65,
0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x74, 0x68, 0x52, 0x65, 0x67,
0x65, 0x78, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x18, 0x04, 0x20,
0x03, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x12, 0x4c, 0x0a, 0x06,
0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x68,
0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0xaa, 0x01, 0x0a,
0x0a, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x07, 0x73,
0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x68,
0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e,
0x61, 0x75, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x73,
0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x48, 0x65, 0x61, 0x64,
0x65, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x6f,
0x72, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09,
0x70, 0x6f, 0x72, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x4e, 0x0a, 0x07, 0x65, 0x78, 0x63,
0x6c, 0x75, 0x64, 0x65, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x68, 0x61, 0x73,
0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x61, 0x75,
0x74, 0x68, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x63, 0x6c, 0x75,
0x64, 0x65, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65,
0x52, 0x07, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x22, 0xfd, 0x01, 0x0a, 0x15, 0x45, 0x78,
0x63, 0x6c, 0x75, 0x64, 0x65, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52,
0x75, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x65, 0x78, 0x61, 0x63,
0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x74, 0x68, 0x45, 0x78, 0x61,
0x63, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69,
0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x61, 0x74, 0x68, 0x50, 0x72, 0x65,
0x66, 0x69, 0x78, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x72, 0x65, 0x67, 0x65,
0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x74, 0x68, 0x52, 0x65, 0x67,
0x65, 0x78, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x18, 0x04, 0x20,
0x03, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x12, 0x4c, 0x0a, 0x06,
0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x68,
0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e,
0x61, 0x75, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x73,
0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x48, 0x65, 0x61, 0x64,
0x65, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x6f,
0x72, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09,
0x70, 0x6f, 0x72, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x22, 0xb9, 0x01, 0x0a, 0x15, 0x44, 0x65,
0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x48, 0x65, 0x61,
0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65,
0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e,
0x74, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x78, 0x61, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
0x52, 0x05, 0x65, 0x78, 0x61, 0x63, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69,
0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12,
0x16, 0x0a, 0x06, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52,
0x06, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x12, 0x14, 0x0a, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78,
0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x12, 0x16, 0x0a,
0x06, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69,
0x6e, 0x76, 0x65, 0x72, 0x74, 0x2a, 0x43, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12,
0x16, 0x0a, 0x12, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43,
0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x43, 0x54, 0x49, 0x4f,
0x4e, 0x5f, 0x44, 0x45, 0x4e, 0x59, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x41, 0x43, 0x54, 0x49,
0x4f, 0x4e, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x10, 0x02, 0x42, 0x98, 0x02, 0x0a, 0x21, 0x63,
0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e,
0x73, 0x75, 0x6c, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31,
0x42, 0x17, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73,
0x69, 0x6f, 0x6e, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x43, 0x67, 0x69, 0x74,
0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72,
0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2d, 0x70,
0x75, 0x62, 0x6c, 0x69, 0x63, 0x2f, 0x70, 0x62, 0x61, 0x75, 0x74, 0x68, 0x2f, 0x76, 0x32, 0x62,
0x65, 0x74, 0x61, 0x31, 0x3b, 0x61, 0x75, 0x74, 0x68, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31,
0xa2, 0x02, 0x03, 0x48, 0x43, 0x41, 0xaa, 0x02, 0x1d, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f,
0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x2e, 0x56,
0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0xca, 0x02, 0x1d, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f,
0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x41, 0x75, 0x74, 0x68, 0x5c, 0x56,
0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0xe2, 0x02, 0x29, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f,
0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x41, 0x75, 0x74, 0x68, 0x5c, 0x56,
0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
0x74, 0x61, 0xea, 0x02, 0x20, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a,
0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x41, 0x75, 0x74, 0x68, 0x3a, 0x3a, 0x56, 0x32,
0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x61, 0x75, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x53, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x52, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x5b, 0x0a, 0x11,
0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x75, 0x6c, 0x65,
0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63,
0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x2e,
0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x10, 0x64, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x22, 0xec, 0x01, 0x0a, 0x06, 0x53, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79,
0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x64, 0x65,
0x6e, 0x74, 0x69, 0x74, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d,
0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61,
0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x74, 0x69,
0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x61, 0x72, 0x74,
0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x65, 0x65, 0x72, 0x18, 0x04, 0x20,
0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x65, 0x65, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x61, 0x6d,
0x65, 0x6e, 0x65, 0x73, 0x73, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0d, 0x73, 0x61, 0x6d, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x47, 0x72, 0x6f, 0x75, 0x70,
0x12, 0x46, 0x0a, 0x07, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x18, 0x06, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x2c, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f,
0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61,
0x31, 0x2e, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52,
0x07, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x22, 0xab, 0x01, 0x0a, 0x0d, 0x45, 0x78, 0x63,
0x6c, 0x75, 0x64, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x64,
0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0c, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12,
0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1c, 0x0a,
0x09, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
0x52, 0x09, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x70,
0x65, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x65, 0x65, 0x72, 0x12,
0x25, 0x0a, 0x0e, 0x73, 0x61, 0x6d, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x5f, 0x67, 0x72, 0x6f, 0x75,
0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x61, 0x6d, 0x65, 0x6e, 0x65, 0x73,
0x73, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x22, 0xc7, 0x02, 0x0a, 0x0f, 0x44, 0x65, 0x73, 0x74, 0x69,
0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61,
0x74, 0x68, 0x5f, 0x65, 0x78, 0x61, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
0x70, 0x61, 0x74, 0x68, 0x45, 0x78, 0x61, 0x63, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x74,
0x68, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
0x70, 0x61, 0x74, 0x68, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61,
0x74, 0x68, 0x5f, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
0x70, 0x61, 0x74, 0x68, 0x52, 0x65, 0x67, 0x65, 0x78, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x74,
0x68, 0x6f, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x68,
0x6f, 0x64, 0x73, 0x12, 0x4c, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x05, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e,
0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x62, 0x65,
0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52,
0x75, 0x6c, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65,
0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18,
0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73,
0x12, 0x4e, 0x0a, 0x07, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x18, 0x07, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x34, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f,
0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61,
0x31, 0x2e, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73,
0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x07, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65,
0x22, 0xfd, 0x01, 0x0a, 0x15, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x50, 0x65, 0x72, 0x6d,
0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61,
0x74, 0x68, 0x5f, 0x65, 0x78, 0x61, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
0x70, 0x61, 0x74, 0x68, 0x45, 0x78, 0x61, 0x63, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x61, 0x74,
0x68, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
0x70, 0x61, 0x74, 0x68, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61,
0x74, 0x68, 0x5f, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09,
0x70, 0x61, 0x74, 0x68, 0x52, 0x65, 0x67, 0x65, 0x78, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x74,
0x68, 0x6f, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x68,
0x6f, 0x64, 0x73, 0x12, 0x4c, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x05, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e,
0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x2e, 0x76, 0x32, 0x62, 0x65,
0x74, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52,
0x75, 0x6c, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65,
0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18,
0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73,
0x22, 0xb9, 0x01, 0x0a, 0x15, 0x44, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x52, 0x75, 0x6c, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18,
0x0a, 0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52,
0x07, 0x70, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x78, 0x61, 0x63,
0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x78, 0x61, 0x63, 0x74, 0x12, 0x16,
0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78,
0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x12, 0x14,
0x0a, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x72,
0x65, 0x67, 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x18, 0x07,
0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x6e, 0x76, 0x65, 0x72, 0x74, 0x2a, 0x43, 0x0a, 0x06,
0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x12, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e,
0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0f,
0x0a, 0x0b, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x44, 0x45, 0x4e, 0x59, 0x10, 0x01, 0x12,
0x10, 0x0a, 0x0c, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x41, 0x4c, 0x4c, 0x4f, 0x57, 0x10,
0x02, 0x42, 0x98, 0x02, 0x0a, 0x21, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63,
0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x2e,
0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x42, 0x17, 0x54, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63,
0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f,
0x50, 0x01, 0x5a, 0x43, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68,
0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2f, 0x70, 0x62, 0x61,
0x75, 0x74, 0x68, 0x2f, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x3b, 0x61, 0x75, 0x74, 0x68,
0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0xa2, 0x02, 0x03, 0x48, 0x43, 0x41, 0xaa, 0x02, 0x1d,
0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c,
0x2e, 0x41, 0x75, 0x74, 0x68, 0x2e, 0x56, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0xca, 0x02, 0x1d,
0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c,
0x5c, 0x41, 0x75, 0x74, 0x68, 0x5c, 0x56, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0xe2, 0x02, 0x29,
0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c,
0x5c, 0x41, 0x75, 0x74, 0x68, 0x5c, 0x56, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x5c, 0x47, 0x50,
0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x20, 0x48, 0x61, 0x73, 0x68,
0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x41,
0x75, 0x74, 0x68, 0x3a, 0x3a, 0x56, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@ -50,7 +50,6 @@ message PartitionTrafficPermissions {
// be in the same tenancy as the TrafficPermissions resource.
message Destination {
string identity_name = 1;
string identity_prefix = 2;
}
enum Action {