mirror of
https://github.com/openziti/ziti.git
synced 2026-10-12 23:32:02 +00:00
Policy advisor (#72)
* Add policy advisor CLI. Add ceate/delete config types CLI. * Update to go 1.14
This commit is contained in:
@@ -6,6 +6,7 @@ Ziti 0.13 includes the following:
|
||||
* New APIs to list existing role attributes used by edge routers, identities and services
|
||||
* New APIs to list entities related by polices (such as listing edge routers available to a service via service edge router policies)
|
||||
* Enhancements to the LIST APIs for edge routers, identities and services which allow one to filter by roles
|
||||
* A policy advisor API, which helps analyze policies and current system state to figure out if an identity should be able to use a service and if not, why not
|
||||
* CA Auto Enrollment now allows identities to inherit role attributes from the validating CA
|
||||
* New `identityRole` attributes added to CA entities
|
||||
* New APIs to list and manage Transit Routers
|
||||
@@ -15,6 +16,8 @@ Ziti 0.13 includes the following:
|
||||
* Fabric enhancements
|
||||
* New Xlink framework encapsulating the router capabilities for creating overlay mesh links.
|
||||
* Adjustable Xgress MTU size.
|
||||
* All Ziti Go projects are now being built with Go 1.14
|
||||
* See here for change to Go in 1.14 - https://golang.org/doc/go1.14
|
||||
|
||||
## Making Policies More User Friendly
|
||||
### Listing Role Attributes in Use
|
||||
@@ -139,7 +142,149 @@ Example:
|
||||
id: a949cf80-b11b-4cce-bbb7-d2e4767878a6 name: baz terminator strategy: role attributes: ["development","sales","support"]
|
||||
id: ad95ec7d-6c05-42b6-b278-2a98a7e502df name: bar terminator strategy: role attributes: ["four","three","two"]
|
||||
id: e9673c77-7463-4517-a642-641ef35855cf name: foo terminator strategy: role attributes: ["one","three","two"]
|
||||
|
||||
## Policy Advisor
|
||||
This adds a new operation to the /identities endpoint
|
||||
|
||||
* Endpoint: /identities
|
||||
* New operations
|
||||
* Query related identities: GET /identities/<identity-id>/policy-advice/<service-id>
|
||||
|
||||
This will return the following information about the identity and service:
|
||||
|
||||
* If the identity can dial the service
|
||||
* If the identity can bind the service
|
||||
* How many edge routers the identity has access to
|
||||
* How many edge routers the service can be accessed through
|
||||
* Which edge routers the identity and service have in common (if this is none, then the service can't be accessed by the identity)
|
||||
* Which of the common edge routers are on-line
|
||||
|
||||
Example result:
|
||||
|
||||
{
|
||||
"meta": {},
|
||||
"data": {
|
||||
"identityId": "700347c8-ca3a-4438-9060-68f7255ee4f8",
|
||||
"identity": {
|
||||
"entity": "identities",
|
||||
"id": "700347c8-ca3a-4438-9060-68f7255ee4f8",
|
||||
"name": "ssh-host",
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "./identities/700347c8-ca3a-4438-9060-68f7255ee4f8"
|
||||
}
|
||||
}
|
||||
},
|
||||
"serviceId": "8fa27a3e-ffb1-4bd1-befa-fcd38a6c26b3",
|
||||
"service": {
|
||||
"entity": "services",
|
||||
"id": "8fa27a3e-ffb1-4bd1-befa-fcd38a6c26b3",
|
||||
"name": "ssh",
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "./services/8fa27a3e-ffb1-4bd1-befa-fcd38a6c26b3"
|
||||
}
|
||||
}
|
||||
},
|
||||
"isBindAllowed": true,
|
||||
"isDialAllowed": false,
|
||||
"identityRouterCount": 2,
|
||||
"serviceRouterCount": 2,
|
||||
"commonRouters": [
|
||||
{
|
||||
"entity": "edge-routers",
|
||||
"id": "43d220d8-860e-4d80-a25c-97322a7326b4",
|
||||
"name": "us-west-1",
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "./edge-routers/43d220d8-860e-4d80-a25c-97322a7326b4"
|
||||
}
|
||||
},
|
||||
"isOnline": false
|
||||
},
|
||||
{
|
||||
"entity": "edge-routers",
|
||||
"id": "8c118857-c12e-430d-9109-c31f535933f6",
|
||||
"name": "us-east-1",
|
||||
"_links": {
|
||||
"self": {
|
||||
"href": "./edge-routers/8c118857-c12e-430d-9109-c31f535933f6"
|
||||
}
|
||||
},
|
||||
"isOnline": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
The CLI has also been updated with a new policy-advisor common.
|
||||
|
||||
Examples:
|
||||
|
||||
# Inspect all identities for policy issues
|
||||
ziti edge controller policy-advisor identities
|
||||
|
||||
# Inspect just the jsmith-laptop identity for policy issues with all services that the identity can access
|
||||
ziti edge controller policy-advisor identities jsmith-laptop
|
||||
|
||||
# Inspect the jsmith-laptop identity for issues related to the ssh service
|
||||
ziti edge controller policy-advisor identities jsmith-laptop ssh
|
||||
|
||||
# Inspect all services for policy issues
|
||||
ziti edge controller policy-advisor services
|
||||
|
||||
# Inspect just the ssh service for policy issues for all identities the service can access
|
||||
ziti edge controller policy-advisor services ssh
|
||||
|
||||
# Inspect the ssh service for issues related to the jsmith-laptop identity
|
||||
ziti edge controller policy-advisor identities ssh jsmith-laptop
|
||||
|
||||
Some example output of the CLI:
|
||||
|
||||
$ ec policy-advisor identities -q
|
||||
ERROR: mlapin-laptop (1) -> ssh-backup (0) Common Routers: (0/0) Dial: Y Bind: N
|
||||
- Service has no edge routers assigned. Adjust service edge router policies.
|
||||
|
||||
ERROR: mlapin-laptop (1) -> ssh (2) Common Routers: (0/0) Dial: Y Bind: N
|
||||
- Identity and services have no edge routers in common. Adjust edge router policies and/or service edge router policies.
|
||||
|
||||
ERROR: ndaniels-laptop (1) -> ssh-backup (0) Common Routers: (0/0) Dial: Y Bind: N
|
||||
- Service has no edge routers assigned. Adjust service edge router policies.
|
||||
|
||||
ERROR: ndaniels-laptop (1) -> ssh (2) Common Routers: (0/1) Dial: Y Bind: N
|
||||
- Common edge routers are all off-line. Bring routers back on-line or adjust edge router policies and/or service edge router policies to increase common router pool.
|
||||
|
||||
ERROR: Default Admin
|
||||
- Identity does not have access to any services. Adjust service policies.
|
||||
|
||||
ERROR: jsmith-laptop (2) -> ssh-backup (0) Common Routers: (0/0) Dial: Y Bind: N
|
||||
- Service has no edge routers assigned. Adjust service edge router policies.
|
||||
|
||||
OKAY : jsmith-laptop (2) -> ssh (2) Common Routers: (1/2) Dial: Y Bind: N
|
||||
|
||||
ERROR: ssh-host (2) -> ssh-backup (0) Common Routers: (0/0) Dial: N Bind: Y
|
||||
- Service has no edge routers assigned. Adjust service edge router policies.
|
||||
|
||||
OKAY : ssh-host (2) -> ssh (2) Common Routers: (1/2) Dial: N Bind: Y
|
||||
|
||||
ERROR: aortega-laptop
|
||||
- Identity does not have access to any services. Adjust service policies.
|
||||
|
||||
ERROR: djones-laptop (0) -> ssh-backup (0) Common Routers: (0/0) Dial: Y Bind: N
|
||||
- Identity has no edge routers assigned. Adjust edge router policies.
|
||||
- Service has no edge routers assigned. Adjust service edge router policies.
|
||||
|
||||
ERROR: djones-laptop (0) -> ssh (2) Common Routers: (0/0) Dial: Y Bind: N
|
||||
- Identity has no edge routers assigned. Adjust edge router policies.
|
||||
|
||||
$ ec policy-advisor identities aortega-laptop ssh-backup -q
|
||||
Found identities with id 70567104-d4bd-45f1-8179-bd1e6ab8751f for name aortega-laptop
|
||||
Found services with id 46e94977-0efc-4e7d-b9ae-cc8df1c95fc1 for name ssh-backup
|
||||
ERROR: aortega-laptop (0) -> ssh-backup (0) Common Routers: (0/0) Dial: N Bind: N
|
||||
- No access to service. Adjust service policies.
|
||||
- Identity has no edge routers assigned. Adjust edge router policies.
|
||||
- Service has no edge routers assigned. Adjust service edge router policies.
|
||||
|
||||
## CA Auto Enrollment Identity Attributes
|
||||
|
||||
Identities that are enrolled via a CA can now inherit a static list of identity role attributes. The normal create,
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
module github.com/netfoundry/ziti-cmd
|
||||
|
||||
go 1.13
|
||||
go 1.14
|
||||
|
||||
//replace github.com/netfoundry/ziti-foundation => ../ziti-foundation
|
||||
// replace github.com/netfoundry/ziti-foundation => ../ziti-foundation
|
||||
|
||||
//replace github.com/netfoundry/ziti-fabric => ../ziti-fabric
|
||||
// replace github.com/netfoundry/ziti-fabric => ../ziti-fabric
|
||||
|
||||
//replace github.com/netfoundry/ziti-sdk-golang => ../ziti-sdk-golang
|
||||
//replace github.com/netfoundry/ziti-edge => ../ziti-edge
|
||||
// replace github.com/netfoundry/ziti-sdk-golang => ../ziti-sdk-golang
|
||||
|
||||
// replace github.com/netfoundry/ziti-edge => ../ziti-edge
|
||||
|
||||
require (
|
||||
github.com/Jeffail/gabs v1.4.0
|
||||
@@ -23,10 +24,10 @@ require (
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||
github.com/keybase/go-ps v0.0.0-20190827175125-91aafc93ba19
|
||||
github.com/michaelquigley/pfxlog v0.0.0-20190813191113-2be43bd0dccc
|
||||
github.com/netfoundry/ziti-edge v0.12.26
|
||||
github.com/netfoundry/ziti-fabric v0.11.10
|
||||
github.com/netfoundry/ziti-edge v0.13.0
|
||||
github.com/netfoundry/ziti-fabric v0.11.11
|
||||
github.com/netfoundry/ziti-foundation v0.9.6
|
||||
github.com/netfoundry/ziti-sdk-golang v0.11.12
|
||||
github.com/netfoundry/ziti-sdk-golang v0.11.14
|
||||
github.com/pborman/uuid v1.2.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0
|
||||
|
||||
@@ -224,16 +224,14 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/netfoundry/secretstream v0.1.1 h1:z6T7B5DuOtr9O+SmKaS9DzwOoGLYtgvyTzuPuR5FO7I=
|
||||
github.com/netfoundry/secretstream v0.1.1/go.mod h1:uasYkYSp0MmNSlKOWJ2sVzxPms8e58TS4ENq4yro86k=
|
||||
github.com/netfoundry/ziti-edge v0.12.26 h1:z8TsTiEUVZ00LBsJDU7JoOBFsI1OQwVXrad9mtH7YnE=
|
||||
github.com/netfoundry/ziti-edge v0.12.26/go.mod h1:PmctiYp1M9bP5taqWChTFIhsVnXbZwN6cEY9/xql1qw=
|
||||
github.com/netfoundry/ziti-fabric v0.11.10 h1:LLzsAAjUSBU3WAa+4RgSR52s34XI1ol+UF5sOIU7LzU=
|
||||
github.com/netfoundry/ziti-fabric v0.11.10/go.mod h1:Zb4n8ZwksX60BfBATVTY7XtN+8zsALBaVyNn1VBeYCY=
|
||||
github.com/netfoundry/ziti-foundation v0.9.4 h1:0IsdbPTKJLbGNgq5gWFFwj/FYq+n9pnee69RGfXKli0=
|
||||
github.com/netfoundry/ziti-foundation v0.9.4/go.mod h1:GN+ALX+7zr1e4vcI2j6VRbaDVcF0wnuih0p9vj22Of0=
|
||||
github.com/netfoundry/ziti-edge v0.13.0 h1:bSW00f6BdgIjwv1VnrJSEwpX0ROoJIRUy+NfQ7ck1VI=
|
||||
github.com/netfoundry/ziti-edge v0.13.0/go.mod h1:OvbtCV8qdGUEMesirROfpF8jneuJkXC4Dye5Pw23n64=
|
||||
github.com/netfoundry/ziti-fabric v0.11.11 h1:G6xaDziZUPO5W1bzxlSp7toJ95CkXpkhA9BIxdnsUcM=
|
||||
github.com/netfoundry/ziti-fabric v0.11.11/go.mod h1:b4KUJ05JJkK9jmIlXy4TODXIgLHTdoruPe0EJ5HJHPg=
|
||||
github.com/netfoundry/ziti-foundation v0.9.6 h1:rsZlM05qAa1PERTqcM4YeCilbCd1i95GbGUzh9OdgKU=
|
||||
github.com/netfoundry/ziti-foundation v0.9.6/go.mod h1:desAxe5jN/V/+cYe99TGEqxtMVdlJ1HfSRzCmuPJD7M=
|
||||
github.com/netfoundry/ziti-sdk-golang v0.11.12 h1:AopEehvVeW3sl9zCzWnGJThbcL3jTC4Sfu5VA3kWkIY=
|
||||
github.com/netfoundry/ziti-sdk-golang v0.11.12/go.mod h1:Wjd4fZxjbo2/cVDyq17KxAatqGzAjtXKiyk9XoxDG8s=
|
||||
github.com/netfoundry/ziti-sdk-golang v0.11.14 h1:C9vRKPW32Kuld/+vpRKEubIQferQNqlhq5FMUL5MLOM=
|
||||
github.com/netfoundry/ziti-sdk-golang v0.11.14/go.mod h1:YJZ2s/W+o+zUuizRCfKQton+PaNP7l9R+xHVuzcIaAY=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/oleiade/reflections v1.0.0 h1:0ir4pc6v8/PJ0yw5AEtMddfXpWBXg9cnG7SgSoJuCgY=
|
||||
github.com/oleiade/reflections v1.0.0/go.mod h1:RbATFBbKYkVdqmSFtx13Bb/tVhR0lgOBXunWTZKeL4w=
|
||||
|
||||
@@ -51,6 +51,21 @@ func mapNameToID(entityType string, val string) (string, error) {
|
||||
return entityId, nil
|
||||
}
|
||||
|
||||
func mapIdToName(entityType string, val string) (string, error) {
|
||||
list, err := filterEntitiesOfType(entityType, fmt.Sprintf(`id="%s"`, val), false, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(list) < 1 {
|
||||
return "", errors.Errorf("no %v found for id %v", entityType, val)
|
||||
}
|
||||
|
||||
entity := list[0]
|
||||
name, _ := entity.Path("name").Data().(string)
|
||||
return name, nil
|
||||
}
|
||||
|
||||
func mapNamesToIDs(entityType string, list ...string) ([]string, error) {
|
||||
var result []string
|
||||
for _, val := range list {
|
||||
|
||||
@@ -41,6 +41,7 @@ func newCreateCmd(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Com
|
||||
cmd.AddCommand(newCreateAuthenticatorCmd(f, out, errOut))
|
||||
cmd.AddCommand(newCreateCaCmd(f, out, errOut))
|
||||
cmd.AddCommand(newCreateConfigCmd(f, out, errOut))
|
||||
cmd.AddCommand(newCreateConfigTypeCmd(f, out, errOut))
|
||||
cmd.AddCommand(newCreateEdgeRouterCmd(f, out, errOut))
|
||||
cmd.AddCommand(newCreateEdgeRouterPolicyCmd(f, out, errOut))
|
||||
cmd.AddCommand(newCreateTerminatorCmd(f, out, errOut))
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
Copyright NetFoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package edge_controller
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/Jeffail/gabs"
|
||||
"github.com/netfoundry/ziti-cmd/ziti/cmd/ziti/cmd/common"
|
||||
cmdutil "github.com/netfoundry/ziti-cmd/ziti/cmd/ziti/cmd/factory"
|
||||
cmdhelper "github.com/netfoundry/ziti-cmd/ziti/cmd/ziti/cmd/helpers"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
type createConfigTypeOptions struct {
|
||||
commonOptions
|
||||
}
|
||||
|
||||
// newCreateConfigTypeCmd creates the 'edge controller create service-policy' command
|
||||
func newCreateConfigTypeCmd(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command {
|
||||
options := &createConfigTypeOptions{
|
||||
commonOptions: commonOptions{
|
||||
CommonOptions: common.CommonOptions{Factory: f, Out: out, Err: errOut},
|
||||
},
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "config-type <name> <JSON schema>",
|
||||
Short: "creates a config type managed by the Ziti Edge Controller",
|
||||
Long: "creates a config type managed by the Ziti Edge Controller",
|
||||
Args: cobra.RangeArgs(1, 2),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
options.Cmd = cmd
|
||||
options.Args = args
|
||||
err := runCreateConfigType(options)
|
||||
cmdhelper.CheckErr(err)
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
// allow interspersing positional args and flags
|
||||
cmd.Flags().SetInterspersed(true)
|
||||
cmd.Flags().BoolVarP(&options.OutputJSONResponse, "output-json", "j", false, "Output the full JSON response from the Ziti Edge Controller")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// runCreateConfigType create a new configType on the Ziti Edge Controller
|
||||
func runCreateConfigType(o *createConfigTypeOptions) error {
|
||||
var schemaMap map[string]interface{}
|
||||
|
||||
if len(o.Args) == 2 {
|
||||
schemaMap = map[string]interface{}{}
|
||||
if err := json.Unmarshal([]byte(o.Args[1]), &schemaMap); err != nil {
|
||||
fmt.Printf("Attempted to parse: %v\n", o.Args[1])
|
||||
fmt.Printf("Failing parsing JSON: %+v\n", err)
|
||||
return errors.Errorf("unable to parse data as json: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
entityData := gabs.New()
|
||||
setJSONValue(entityData, o.Args[0], "name")
|
||||
if schemaMap != nil {
|
||||
setJSONValue(entityData, schemaMap, "schema")
|
||||
}
|
||||
|
||||
result, err := createEntityOfType("config-types", entityData.String(), &o.commonOptions)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
configTypeId := result.S("data", "id").Data()
|
||||
|
||||
if _, err := fmt.Fprintf(o.Out, "%v\n", configTypeId); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -54,6 +54,7 @@ func newDeleteCmd(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Com
|
||||
cmd.AddCommand(newDeleteAuthenticatorCmd("authenticator", newOptions()))
|
||||
cmd.AddCommand(newDeleteCmdForEntityType("ca", runDeleteEntityOfType, newOptions()))
|
||||
cmd.AddCommand(newDeleteCmdForEntityType("config", runDeleteEntityOfType, newOptions()))
|
||||
cmd.AddCommand(newDeleteCmdForEntityType("config-type", runDeleteEntityOfType, newOptions()))
|
||||
cmd.AddCommand(newDeleteCmdForEntityType("edge-router", runDeleteEntityOfType, newOptions()))
|
||||
cmd.AddCommand(newDeleteCmdForEntityType("edge-router-policy", runDeleteEntityOfType, newOptions()))
|
||||
cmd.AddCommand(newDeleteCmdForEntityType("identity", runDeleteEntityOfType, newOptions()))
|
||||
|
||||
@@ -300,7 +300,11 @@ func filterSubEntitiesOfType(entityType, subType, entityId, filter string, o *co
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return jsonParsed.S("data").Children()
|
||||
children, err := jsonParsed.S("data").Children()
|
||||
if err == gabs.ErrNotObjOrArray {
|
||||
return nil, nil
|
||||
}
|
||||
return children, err
|
||||
}
|
||||
|
||||
func runListEdgeRouters(roleFilters []string, roleSemantic string, options *commonOptions) error {
|
||||
|
||||
@@ -0,0 +1,402 @@
|
||||
/*
|
||||
Copyright NetFoundry, Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package edge_controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/Jeffail/gabs"
|
||||
"github.com/netfoundry/ziti-cmd/ziti/cmd/ziti/cmd/common"
|
||||
cmdutil "github.com/netfoundry/ziti-cmd/ziti/cmd/ziti/cmd/factory"
|
||||
cmdhelper "github.com/netfoundry/ziti-cmd/ziti/cmd/ziti/cmd/helpers"
|
||||
"github.com/netfoundry/ziti-cmd/ziti/cmd/ziti/util"
|
||||
"github.com/spf13/cobra"
|
||||
"io"
|
||||
)
|
||||
|
||||
// newPolicyAdvisor creates a command object for the "controller policy-advisor" command
|
||||
func newPolicyAdivsorCmd(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "policy-advisor",
|
||||
Short: "runs sanity checks on various policy related entities managed by the Ziti Edge Controller",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := cmd.Help()
|
||||
cmdhelper.CheckErr(err)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.AddCommand(newPolicyAdvisorIdentitiesCmd(f, out, errOut))
|
||||
cmd.AddCommand(newPolicyAdvisorServicesCmd(f, out, errOut))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
type policyAdvisorOptions struct {
|
||||
commonOptions
|
||||
quiet bool
|
||||
}
|
||||
|
||||
// newPolicyAdvisorIdentitiesCmd creates the 'edge controller policy-advisor identities' command
|
||||
func newPolicyAdvisorIdentitiesCmd(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command {
|
||||
options := &policyAdvisorOptions{
|
||||
commonOptions: commonOptions{
|
||||
CommonOptions: common.CommonOptions{Factory: f, Out: out, Err: errOut},
|
||||
},
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "identities <identity name or id>? <service name or id>?",
|
||||
Short: "checks policies/connectivity between identities and services",
|
||||
Args: cobra.RangeArgs(0, 2),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
options.Cmd = cmd
|
||||
options.Args = args
|
||||
err := runIdentitiesPolicyAdvisor(options)
|
||||
cmdhelper.CheckErr(err)
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
// allow interspersing positional args and flags
|
||||
cmd.Flags().SetInterspersed(true)
|
||||
cmd.Flags().BoolVarP(&options.OutputJSONResponse, "output-json", "j", false, "Output the full JSON response from the Ziti Edge Controller")
|
||||
cmd.Flags().BoolVarP(&options.quiet, "quiet", "q", false, "Minimize output by hiding header")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// newPolicyAdvisorServicesCmd creates the 'edge controller policy-advisor services' command
|
||||
func newPolicyAdvisorServicesCmd(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command {
|
||||
options := &policyAdvisorOptions{
|
||||
commonOptions: commonOptions{
|
||||
CommonOptions: common.CommonOptions{Factory: f, Out: out, Err: errOut},
|
||||
},
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "services <service name or id>? <identity name or id>?",
|
||||
Short: "checks policies/connectivity between services and identities ",
|
||||
Args: cobra.RangeArgs(0, 2),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
options.Cmd = cmd
|
||||
options.Args = args
|
||||
err := runServicesPolicyAdvisor(options)
|
||||
cmdhelper.CheckErr(err)
|
||||
},
|
||||
SuggestFor: []string{},
|
||||
}
|
||||
|
||||
// allow interspersing positional args and flags
|
||||
cmd.Flags().SetInterspersed(true)
|
||||
cmd.Flags().BoolVarP(&options.OutputJSONResponse, "output-json", "j", false, "Output the full JSON response from the Ziti Edge Controller")
|
||||
cmd.Flags().BoolVarP(&options.quiet, "quiet", "q", false, "Minimize output by hiding header")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// runIdentitiesPolicyAdvisor create a new policyAdvisor on the Ziti Edge Controller
|
||||
func runIdentitiesPolicyAdvisor(o *policyAdvisorOptions) error {
|
||||
if len(o.Args) > 0 {
|
||||
identityId, err := mapNameToID("identities", o.Args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(o.Args) > 1 {
|
||||
serviceId, err := mapNameToID("services", o.Args[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := outputHeader(o); err != nil {
|
||||
return err
|
||||
}
|
||||
return runPolicyAdvisorForIdentityAndService(identityId, serviceId, o)
|
||||
}
|
||||
|
||||
if err := outputHeader(o); err != nil {
|
||||
return err
|
||||
}
|
||||
return runPolicyAdvisorForIdentity(identityId, o)
|
||||
}
|
||||
|
||||
if err := outputHeader(o); err != nil {
|
||||
return err
|
||||
}
|
||||
return runPolicyAdvisorForIdentities(o)
|
||||
}
|
||||
|
||||
// runServicesPolicyAdvisor create a new policyAdvisor on the Ziti Edge Controller
|
||||
func runServicesPolicyAdvisor(o *policyAdvisorOptions) error {
|
||||
if len(o.Args) > 0 {
|
||||
serviceId, err := mapNameToID("services", o.Args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(o.Args) > 1 {
|
||||
identityId, err := mapNameToID("identities", o.Args[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := outputHeader(o); err != nil {
|
||||
return err
|
||||
}
|
||||
return runPolicyAdvisorForIdentityAndService(identityId, serviceId, o)
|
||||
}
|
||||
|
||||
if err := outputHeader(o); err != nil {
|
||||
return err
|
||||
}
|
||||
return runPolicyAdvisorForService(serviceId, o)
|
||||
}
|
||||
|
||||
if err := outputHeader(o); err != nil {
|
||||
return err
|
||||
}
|
||||
return runPolicyAdvisorForServices(o)
|
||||
}
|
||||
|
||||
func outputHeader(o *policyAdvisorOptions) error {
|
||||
if o.quiet {
|
||||
return nil
|
||||
}
|
||||
_, err := fmt.Fprintf(o.Out, "\n"+
|
||||
"Policy General Guidelines\n"+
|
||||
" In order for an identity to dial or bind a service, the following must be true:\n"+
|
||||
" - The identity must have access to the service via a service policy of the correct type (dial or bind)\n"+
|
||||
" - The identity must have acces to at least one on-line edge router via an edge router policy\n"+
|
||||
" - The service must have access to at least one on-line edge router via a service edge router policy\n"+
|
||||
" - There must be at least one on-line edge router that both the identity and service have access to.\n"+
|
||||
"\n"+
|
||||
"Policy Advisor Output Guide:\n"+
|
||||
" STATUS = The status of the identity -> service reachability. Will be OKAY or ERROR. \n"+
|
||||
" ID = identity name\n"+
|
||||
" ID ROUTERS = number of routers accessible to the identity via edge router policies.\n"+
|
||||
" - See edge router polices for an identity: ziti edge controller list identity edge-router-policies <identity>\n"+
|
||||
" SVC = service name\n"+
|
||||
" SVC ROUTERS = number of routers accessible to the service via service edge router policies.\n"+
|
||||
" - See service edge router policies for a service with: ziti edge controller list service service-edge-router-policies <service>\n"+
|
||||
" ONLINE COMMON ROUTERS = number of routers the identity and service have in common which are online.\n"+
|
||||
" COMMON ROUTERS = number of routers (online or offline) the identity and service have in common.\n"+
|
||||
" DIAL_OK = indicates if the identity has permission to dial the service.\n"+
|
||||
" - See service polices for a service : ziti edge controller list service service-policies <service>\n"+
|
||||
" - See service polices for an identity: ziti edge controller list identity service-policies <identity>\n"+
|
||||
" BIND_OK = indicates if the identity has permission to bind the service.\n"+
|
||||
" ERROR_LIST = if the status is ERROR, error details will be listed on the following lines\n"+
|
||||
"\n"+
|
||||
"Output format: STATUS: ID (ID ROUTERS) -> SVC (SVC ROUTERS) Common Routers: (ONLINE COMMON ROUTERS/COMMON ROUTERS) Dial: DIAL_OK Bind: BIND_OK. ERROR_LIST\n"+
|
||||
"-------------------------------------------------------------------------------\n")
|
||||
return err
|
||||
}
|
||||
|
||||
func runPolicyAdvisorForIdentities(o *policyAdvisorOptions) error {
|
||||
skip := 0
|
||||
done := false
|
||||
for !done {
|
||||
filter := fmt.Sprintf(`true skip %v limit 2`, skip)
|
||||
children, err := filterEntitiesOfType("identities", filter, false, o.Out)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, child := range children {
|
||||
identityId, _ := child.S("id").Data().(string)
|
||||
if err := runPolicyAdvisorForIdentity(identityId, o); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
skip += len(children)
|
||||
if len(children) == 0 {
|
||||
done = true
|
||||
}
|
||||
}
|
||||
|
||||
if skip == 0 {
|
||||
_, err := fmt.Fprintln(o.Out, "No identities found")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runPolicyAdvisorForServices(o *policyAdvisorOptions) error {
|
||||
skip := 0
|
||||
done := false
|
||||
for !done {
|
||||
filter := fmt.Sprintf(`true skip %v limit 2`, skip)
|
||||
children, err := filterEntitiesOfType("services", filter, false, o.Out)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, child := range children {
|
||||
serviceId, _ := child.S("id").Data().(string)
|
||||
if err := runPolicyAdvisorForService(serviceId, o); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
skip += len(children)
|
||||
if len(children) == 0 {
|
||||
done = true
|
||||
}
|
||||
}
|
||||
|
||||
if skip == 0 {
|
||||
_, err := fmt.Fprintln(o.Out, "No services found")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runPolicyAdvisorForIdentity(identityId string, o *policyAdvisorOptions) error {
|
||||
skip := 0
|
||||
done := false
|
||||
for !done {
|
||||
filter := "true limit 2"
|
||||
filter = fmt.Sprintf(`true skip %v limit 2`, skip)
|
||||
children, err := filterSubEntitiesOfType("identities", "services", identityId, filter, &o.commonOptions)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, child := range children {
|
||||
serviceId, _ := child.S("id").Data().(string)
|
||||
if err := runPolicyAdvisorForIdentityAndService(identityId, serviceId, o); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
skip += len(children)
|
||||
if len(children) == 0 {
|
||||
done = true
|
||||
}
|
||||
}
|
||||
|
||||
if skip == 0 {
|
||||
identityName, err := mapIdToName("identities", identityId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = fmt.Fprintf(o.Out, "ERROR: %v %v\n\n", identityName, "\n - Identity does not have access to any services. Adjust service policies.")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runPolicyAdvisorForService(serviceId string, o *policyAdvisorOptions) error {
|
||||
skip := 0
|
||||
done := false
|
||||
for !done {
|
||||
filter := "true limit 2"
|
||||
filter = fmt.Sprintf(`true skip %v limit 2`, skip)
|
||||
children, err := filterSubEntitiesOfType("services", "identities", serviceId, filter, &o.commonOptions)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, child := range children {
|
||||
identityId, _ := child.S("id").Data().(string)
|
||||
if err := runPolicyAdvisorForIdentityAndService(identityId, serviceId, o); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
skip += len(children)
|
||||
if len(children) == 0 {
|
||||
done = true
|
||||
}
|
||||
}
|
||||
|
||||
if skip == 0 {
|
||||
serviceName, err := mapIdToName("services", serviceId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = fmt.Fprintf(o.Out, "ERROR: %v %v\n\n", serviceName, "\n - Service is not accessible by any identities. Adjust service policies.")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func runPolicyAdvisorForIdentityAndService(identityId, serviceId string, o *policyAdvisorOptions) error {
|
||||
result, err := util.EdgeControllerList("identities/"+identityId+"/policy-advice/"+serviceId, nil, o.OutputJSONResponse, o.Out)
|
||||
if err != nil || o.OutputJSONResponse {
|
||||
return err
|
||||
}
|
||||
|
||||
identityName, _ := result.S("data", "identity", "name").Data().(string)
|
||||
serviceName, _ := result.S("data", "service", "name").Data().(string)
|
||||
isBindAllowed, _ := result.S("data", "isBindAllowed").Data().(bool)
|
||||
isDialAllowed, _ := result.S("data", "isDialAllowed").Data().(bool)
|
||||
identityRouterCount, _ := result.S("data", "identityRouterCount").Data().(float64)
|
||||
serviceRouterCount, _ := result.S("data", "serviceRouterCount").Data().(float64)
|
||||
|
||||
commonRouters, err := result.S("data", "commonRouters").Children()
|
||||
if err != nil && err != gabs.ErrNotObjOrArray {
|
||||
return err
|
||||
}
|
||||
|
||||
commonCount := len(commonRouters)
|
||||
onlineCount := 0
|
||||
for _, commonRouter := range commonRouters {
|
||||
isOnline := commonRouter.S("isOnline").Data().(bool)
|
||||
if isOnline {
|
||||
onlineCount++
|
||||
}
|
||||
}
|
||||
|
||||
status := "OKAY "
|
||||
detail := ""
|
||||
if !isBindAllowed && !isDialAllowed {
|
||||
status = "ERROR"
|
||||
detail += "\n - No access to service. Adjust service policies."
|
||||
}
|
||||
|
||||
if identityRouterCount < 1 {
|
||||
status = "ERROR"
|
||||
detail += "\n - Identity has no edge routers assigned. Adjust edge router policies."
|
||||
}
|
||||
|
||||
if serviceRouterCount < 1 {
|
||||
status = "ERROR"
|
||||
detail += "\n - Service has no edge routers assigned. Adjust service edge router policies."
|
||||
}
|
||||
|
||||
if identityRouterCount > 0 && serviceRouterCount > 0 {
|
||||
if commonCount < 1 {
|
||||
status = "ERROR"
|
||||
detail += "\n - Identity and services have no edge routers in common. Adjust edge router policies and/or service edge router policies."
|
||||
} else if onlineCount < 1 {
|
||||
status = "ERROR"
|
||||
detail += "\n - Common edge routers are all off-line. Bring routers back on-line or adjust edge router policies and/or service edge router policies to increase common router pool."
|
||||
}
|
||||
}
|
||||
|
||||
dialStatus := "Y"
|
||||
if !isDialAllowed {
|
||||
dialStatus = "N"
|
||||
}
|
||||
|
||||
bindStatus := "Y"
|
||||
if !isBindAllowed {
|
||||
bindStatus = "N"
|
||||
}
|
||||
|
||||
_, err = fmt.Fprintf(o.Out, "%v: %v (%v) -> %v (%v) Common Routers: (%v/%v) Dial: %v Bind: %v %v\n\n",
|
||||
status, identityName, identityRouterCount, serviceName, serviceRouterCount, onlineCount, commonCount, dialStatus, bindStatus, detail)
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -48,6 +48,7 @@ func newCmdEdgeController(f cmdutil.Factory, out io.Writer, errOut io.Writer) *c
|
||||
cmd.AddCommand(newListCmd(f, out, errOut))
|
||||
cmd.AddCommand(newUpdateCmd(f, out, errOut))
|
||||
cmd.AddCommand(newVersionCmd(f, out, errOut))
|
||||
cmd.AddCommand(newPolicyAdivsorCmd(f, out, errOut))
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user