2022-12-05 19:12:12 +01:00
package controller
import (
2022-12-05 21:40:42 +01:00
"bytes"
2022-12-05 19:12:12 +01:00
"context"
2022-12-05 21:40:42 +01:00
"encoding/json"
2022-12-05 19:12:12 +01:00
"fmt"
"github.com/openziti/edge/rest_management_api_client"
"github.com/openziti/edge/rest_management_api_client/config"
2022-12-05 20:25:49 +01:00
"github.com/openziti/edge/rest_management_api_client/edge_router_policy"
"github.com/openziti/edge/rest_management_api_client/identity"
2022-12-05 22:10:38 +01:00
"github.com/openziti/edge/rest_management_api_client/service"
2022-12-05 22:48:59 +01:00
"github.com/openziti/edge/rest_management_api_client/service_edge_router_policy"
2022-12-05 23:29:35 +01:00
"github.com/openziti/edge/rest_management_api_client/service_policy"
2022-12-05 19:12:12 +01:00
"github.com/openziti/edge/rest_model"
2022-12-05 21:40:42 +01:00
rest_model_edge "github.com/openziti/edge/rest_model"
2022-12-05 20:00:51 +01:00
"github.com/openziti/sdk-golang/ziti"
config2 "github.com/openziti/sdk-golang/ziti/config"
2023-01-13 21:01:34 +01:00
"github.com/openziti/zrok/controller/store"
"github.com/openziti/zrok/controller/zrokEdgeSdk"
"github.com/openziti/zrok/model"
"github.com/openziti/zrok/zrokdir"
2022-12-05 19:12:12 +01:00
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"time"
)
2022-12-05 21:00:22 +01:00
func Bootstrap ( skipCtrl , skipFrontend bool , inCfg * Config ) error {
2022-12-05 19:12:12 +01:00
cfg = inCfg
2022-12-06 18:25:17 +01:00
if v , err := store . Open ( cfg . Store ) ; err == nil {
str = v
} else {
return errors . Wrap ( err , "error opening store" )
}
2022-12-07 18:01:56 +01:00
logrus . Info ( "connecting to the ziti edge management api" )
2022-12-05 20:25:49 +01:00
edge , err := edgeClient ( )
if err != nil {
2022-12-07 18:01:56 +01:00
return errors . Wrap ( err , "error connecting to the ziti edge management api" )
2022-12-05 20:25:49 +01:00
}
2022-12-05 21:40:42 +01:00
var ctrlZId string
2022-12-05 21:00:22 +01:00
if ! skipCtrl {
2022-12-07 18:01:56 +01:00
logrus . Info ( "creating identity for controller ziti access" )
2022-12-05 21:40:42 +01:00
if ctrlZId , err = getIdentityId ( "ctrl" ) ; err == nil {
2022-12-05 21:00:22 +01:00
logrus . Infof ( "controller identity: %v" , ctrlZId )
2022-12-05 21:40:42 +01:00
} else {
ctrlZId , err = bootstrapIdentity ( "ctrl" , edge )
if err != nil {
2022-12-05 21:00:22 +01:00
panic ( err )
}
2022-12-05 21:40:42 +01:00
}
if err := assertIdentity ( ctrlZId , edge ) ; err != nil {
panic ( err )
}
if err := assertErpForIdentity ( "ctrl" , ctrlZId , edge ) ; err != nil {
2022-12-05 20:25:49 +01:00
panic ( err )
}
2022-12-05 20:00:51 +01:00
}
2022-12-05 21:40:42 +01:00
var frontendZId string
2022-12-05 21:00:22 +01:00
if ! skipFrontend {
2022-12-07 18:01:56 +01:00
logrus . Info ( "creating identity for frontend ziti access" )
2022-12-05 21:40:42 +01:00
if frontendZId , err = getIdentityId ( "frontend" ) ; err == nil {
2022-12-05 21:00:22 +01:00
logrus . Infof ( "frontend identity: %v" , frontendZId )
2022-12-05 21:40:42 +01:00
} else {
frontendZId , err = bootstrapIdentity ( "frontend" , edge )
if err != nil {
2022-12-05 21:00:22 +01:00
panic ( err )
}
2022-12-05 21:40:42 +01:00
}
if err := assertIdentity ( frontendZId , edge ) ; err != nil {
panic ( err )
}
if err := assertErpForIdentity ( "frontend" , frontendZId , edge ) ; err != nil {
2022-12-05 20:25:49 +01:00
panic ( err )
}
2022-12-06 18:25:17 +01:00
tx , err := str . Begin ( )
if err != nil {
panic ( err )
}
defer func ( ) { _ = tx . Rollback ( ) } ( )
publicFe , err := str . FindFrontendWithZId ( frontendZId , tx )
if err != nil {
2023-01-04 20:21:23 +01:00
logrus . Warnf ( "missing public frontend for ziti id '%v'; please use 'zrok admin create frontend %v public https://{token}.your.dns.name' to create a frontend instance" , frontendZId , frontendZId )
2022-12-06 18:25:17 +01:00
} else {
if publicFe . PublicName != nil && publicFe . UrlTemplate != nil {
logrus . Infof ( "found public frontend entry '%v' (%v) for ziti identity '%v'" , * publicFe . PublicName , publicFe . Token , frontendZId )
} else {
2022-12-06 20:53:59 +01:00
logrus . Warnf ( "found frontend entry for ziti identity '%v'; missing either public name or url template" , frontendZId )
2022-12-06 18:25:17 +01:00
}
}
2022-12-05 20:00:51 +01:00
}
2022-12-05 19:12:12 +01:00
if err := assertZrokProxyConfigType ( edge ) ; err != nil {
return err
}
2022-12-05 22:48:59 +01:00
var metricsSvcZId string
if metricsSvcZId , err = assertMetricsService ( cfg , edge ) ; err != nil {
return err
}
if err := assertMetricsSerp ( metricsSvcZId , cfg , edge ) ; err != nil {
2022-12-05 22:10:38 +01:00
return err
}
2022-12-05 23:29:35 +01:00
if ! skipCtrl {
if err := assertCtrlMetricsBind ( ctrlZId , metricsSvcZId , edge ) ; err != nil {
return err
}
}
if ! skipFrontend {
if err := assertFrontendMetricsDial ( frontendZId , metricsSvcZId , edge ) ; err != nil {
return err
}
}
2022-12-05 19:12:12 +01:00
return nil
}
func assertZrokProxyConfigType ( edge * rest_management_api_client . ZitiEdgeManagement ) error {
filter := fmt . Sprintf ( "name=\"%v\"" , model . ZrokProxyConfig )
limit := int64 ( 100 )
offset := int64 ( 0 )
listReq := & config . ListConfigTypesParams {
Filter : & filter ,
Limit : & limit ,
Offset : & offset ,
Context : context . Background ( ) ,
}
listReq . SetTimeout ( 30 * time . Second )
listResp , err := edge . Config . ListConfigTypes ( listReq , nil )
if err != nil {
return err
}
if len ( listResp . Payload . Data ) < 1 {
name := model . ZrokProxyConfig
ct := & rest_model . ConfigTypeCreate { Name : & name }
createReq := & config . CreateConfigTypeParams { ConfigType : ct }
createReq . SetTimeout ( 30 * time . Second )
createResp , err := edge . Config . CreateConfigType ( createReq , nil )
if err != nil {
return err
}
logrus . Infof ( "created '%v' config type with id '%v'" , model . ZrokProxyConfig , createResp . Payload . Data . ID )
} else if len ( listResp . Payload . Data ) > 1 {
return errors . Errorf ( "found %d '%v' config types; expected 0 or 1" , len ( listResp . Payload . Data ) , model . ZrokProxyConfig )
} else {
logrus . Infof ( "found '%v' config type with id '%v'" , model . ZrokProxyConfig , * ( listResp . Payload . Data [ 0 ] . ID ) )
}
return nil
}
2022-12-05 20:00:51 +01:00
func getIdentityId ( identityName string ) ( string , error ) {
zif , err := zrokdir . ZitiIdentityFile ( identityName )
if err != nil {
2022-12-05 20:06:53 +01:00
return "" , errors . Wrapf ( err , "error opening identity '%v' from zrokdir" , identityName )
2022-12-05 20:00:51 +01:00
}
zcfg , err := config2 . NewFromFile ( zif )
if err != nil {
2022-12-05 20:06:53 +01:00
return "" , errors . Wrapf ( err , "error loading ziti config from file '%v'" , zif )
2022-12-05 20:00:51 +01:00
}
zctx := ziti . NewContextWithConfig ( zcfg )
id , err := zctx . GetCurrentIdentity ( )
if err != nil {
2022-12-05 20:06:53 +01:00
return "" , errors . Wrapf ( err , "error getting current identity from '%v'" , zif )
2022-12-05 20:00:51 +01:00
}
return id . Id , nil
}
2022-12-05 20:25:49 +01:00
func assertIdentity ( zId string , edge * rest_management_api_client . ZitiEdgeManagement ) error {
filter := fmt . Sprintf ( "id=\"%v\"" , zId )
limit := int64 ( 0 )
offset := int64 ( 0 )
listReq := & identity . ListIdentitiesParams {
Filter : & filter ,
Limit : & limit ,
Offset : & offset ,
}
listReq . SetTimeout ( 30 * time . Second )
listResp , err := edge . Identity . ListIdentities ( listReq , nil )
if err != nil {
return errors . Wrapf ( err , "error listing identities for '%v'" , zId )
}
if len ( listResp . Payload . Data ) != 1 {
2022-12-05 21:03:55 +01:00
return errors . Wrapf ( err , "found %d identities for '%v'" , len ( listResp . Payload . Data ) , zId )
2022-12-05 20:25:49 +01:00
}
logrus . Infof ( "asserted identity '%v'" , zId )
return nil
}
2022-12-05 21:40:42 +01:00
func bootstrapIdentity ( name string , edge * rest_management_api_client . ZitiEdgeManagement ) ( string , error ) {
2022-12-14 20:40:45 +01:00
idc , err := zrokEdgeSdk . CreateIdentity ( name , rest_model_edge . IdentityTypeDevice , nil , edge )
2022-12-05 21:40:42 +01:00
if err != nil {
2022-12-05 22:10:38 +01:00
return "" , errors . Wrapf ( err , "error creating '%v' identity" , name )
2022-12-05 21:40:42 +01:00
}
zId := idc . Payload . Data . ID
2022-12-14 20:40:45 +01:00
cfg , err := zrokEdgeSdk . EnrollIdentity ( zId , edge )
2022-12-05 21:40:42 +01:00
if err != nil {
2022-12-05 22:10:38 +01:00
return "" , errors . Wrapf ( err , "error enrolling '%v' identity" , name )
2022-12-05 21:40:42 +01:00
}
var out bytes . Buffer
enc := json . NewEncoder ( & out )
enc . SetEscapeHTML ( false )
err = enc . Encode ( & cfg )
if err != nil {
return "" , errors . Wrapf ( err , "error encoding identity config '%v'" , name )
}
if err := zrokdir . SaveZitiIdentity ( name , out . String ( ) ) ; err != nil {
return "" , errors . Wrapf ( err , "error saving identity config '%v'" , name )
}
return zId , nil
}
2022-12-05 20:25:49 +01:00
func assertErpForIdentity ( name , zId string , edge * rest_management_api_client . ZitiEdgeManagement ) error {
filter := fmt . Sprintf ( "name=\"%v\" and tags.zrok != null" , name )
limit := int64 ( 0 )
offset := int64 ( 0 )
listReq := & edge_router_policy . ListEdgeRouterPoliciesParams {
Filter : & filter ,
Limit : & limit ,
Offset : & offset ,
}
listReq . SetTimeout ( 30 * time . Second )
listResp , err := edge . EdgeRouterPolicy . ListEdgeRouterPolicies ( listReq , nil )
if err != nil {
return errors . Wrapf ( err , "error listing edge router policies for '%v' (%v)" , name , zId )
}
if len ( listResp . Payload . Data ) != 1 {
2022-12-05 21:00:22 +01:00
logrus . Infof ( "creating erp for '%v' (%v)" , name , zId )
2022-12-14 20:40:45 +01:00
if err := zrokEdgeSdk . CreateEdgeRouterPolicy ( name , zId , edge ) ; err != nil {
2022-12-05 21:00:22 +01:00
return errors . Wrapf ( err , "error creating erp for '%v' (%v)" , name , zId )
}
2022-12-05 20:25:49 +01:00
}
logrus . Infof ( "asserted erps for '%v' (%v)" , name , zId )
return nil
}
2022-12-05 22:10:38 +01:00
2022-12-05 22:48:59 +01:00
func assertMetricsService ( cfg * Config , edge * rest_management_api_client . ZitiEdgeManagement ) ( string , error ) {
filter := fmt . Sprintf ( "name=\"%v\" and tags.zrok != null" , cfg . Metrics . ServiceName )
2022-12-05 22:10:38 +01:00
limit := int64 ( 0 )
offset := int64 ( 0 )
listReq := & service . ListServicesParams {
Filter : & filter ,
Limit : & limit ,
Offset : & offset ,
}
listReq . SetTimeout ( 30 * time . Second )
listResp , err := edge . Service . ListServices ( listReq , nil )
if err != nil {
2022-12-05 22:48:59 +01:00
return "" , errors . Wrapf ( err , "error listing '%v' service" , cfg . Metrics . ServiceName )
}
var svcZId string
if len ( listResp . Payload . Data ) != 1 {
logrus . Infof ( "creating '%v' service" , cfg . Metrics . ServiceName )
2022-12-14 20:40:45 +01:00
svcZId , err = zrokEdgeSdk . CreateService ( "metrics" , nil , nil , edge )
2022-12-05 22:48:59 +01:00
if err != nil {
return "" , errors . Wrapf ( err , "error creating '%v' service" , cfg . Metrics . ServiceName )
}
} else {
svcZId = * listResp . Payload . Data [ 0 ] . ID
}
logrus . Infof ( "asserted '%v' service (%v)" , cfg . Metrics . ServiceName , svcZId )
return svcZId , nil
}
func assertMetricsSerp ( metricsSvcZId string , cfg * Config , edge * rest_management_api_client . ZitiEdgeManagement ) error {
filter := fmt . Sprintf ( "allOf(serviceRoles) = \"@%v\" and allOf(edgeRouterRoles) = \"#all\" and tags.zrok != null" , metricsSvcZId )
limit := int64 ( 0 )
offset := int64 ( 0 )
listReq := & service_edge_router_policy . ListServiceEdgeRouterPoliciesParams {
Filter : & filter ,
Limit : & limit ,
Offset : & offset ,
}
listReq . SetTimeout ( 30 * time . Second )
listResp , err := edge . ServiceEdgeRouterPolicy . ListServiceEdgeRouterPolicies ( listReq , nil )
if err != nil {
return errors . Wrapf ( err , "error listing '%v' serps" , cfg . Metrics . ServiceName )
2022-12-05 22:10:38 +01:00
}
if len ( listResp . Payload . Data ) != 1 {
2022-12-05 22:48:59 +01:00
logrus . Infof ( "creating '%v' serp" , cfg . Metrics . ServiceName )
2022-12-14 20:40:45 +01:00
_ , err := zrokEdgeSdk . CreateServiceEdgeRouterPolicy ( cfg . Metrics . ServiceName , metricsSvcZId , nil , edge )
2022-12-05 22:10:38 +01:00
if err != nil {
2022-12-05 22:48:59 +01:00
return errors . Wrapf ( err , "error creating '%v' serp" , cfg . Metrics . ServiceName )
2022-12-05 22:10:38 +01:00
}
}
2022-12-05 22:48:59 +01:00
logrus . Infof ( "asserted '%v' serp" , cfg . Metrics . ServiceName )
2022-12-05 22:10:38 +01:00
return nil
}
2022-12-05 23:29:35 +01:00
func assertCtrlMetricsBind ( ctrlZId , metricsSvcZId string , edge * rest_management_api_client . ZitiEdgeManagement ) error {
filter := fmt . Sprintf ( "allOf(serviceRoles) = \"@%v\" and allOf(identityRoles) = \"@%v\" and type = 2 and tags.zrok != null" , metricsSvcZId , ctrlZId )
limit := int64 ( 0 )
offset := int64 ( 0 )
listReq := & service_policy . ListServicePoliciesParams {
Filter : & filter ,
Limit : & limit ,
Offset : & offset ,
}
listReq . SetTimeout ( 30 * time . Second )
listResp , err := edge . ServicePolicy . ListServicePolicies ( listReq , nil )
if err != nil {
return errors . Wrapf ( err , "error listing 'ctrl-metrics-bind' service policy" )
}
if len ( listResp . Payload . Data ) != 1 {
logrus . Info ( "creating 'ctrl-metrics-bind' service policy" )
2022-12-14 23:21:56 +01:00
if err = zrokEdgeSdk . CreateServicePolicyBind ( "ctrl-metrics-bind" , metricsSvcZId , ctrlZId , nil , edge ) ; err != nil {
2022-12-05 23:29:35 +01:00
return errors . Wrap ( err , "error creating 'ctrl-metrics-bind' service policy" )
}
}
logrus . Infof ( "asserted 'ctrl-metrics-bind' service policy" )
return nil
}
func assertFrontendMetricsDial ( frontendZId , metricsSvcZId string , edge * rest_management_api_client . ZitiEdgeManagement ) error {
filter := fmt . Sprintf ( "allOf(serviceRoles) = \"@%v\" and allOf(identityRoles) = \"@%v\" and type = 1 and tags.zrok != null" , metricsSvcZId , frontendZId )
limit := int64 ( 0 )
offset := int64 ( 0 )
listReq := & service_policy . ListServicePoliciesParams {
Filter : & filter ,
Limit : & limit ,
Offset : & offset ,
}
listReq . SetTimeout ( 30 * time . Second )
listResp , err := edge . ServicePolicy . ListServicePolicies ( listReq , nil )
if err != nil {
return errors . Wrapf ( err , "error listing 'frontend-metrics-dial' service policy" )
}
if len ( listResp . Payload . Data ) != 1 {
logrus . Info ( "creating 'frontend-metrics-dial' service policy" )
2022-12-14 23:21:56 +01:00
if err = zrokEdgeSdk . CreateServicePolicyDial ( "frontend-metrics-dial" , metricsSvcZId , [ ] string { frontendZId } , nil , edge ) ; err != nil {
2022-12-05 23:29:35 +01:00
return errors . Wrap ( err , "error creating 'frontend-metrics-dial' service policy" )
}
}
logrus . Infof ( "asserted 'frontend-metrics-dial' service policy" )
return nil
}