Skip to content

Commit

Permalink
Merge pull request #419 from openziti/pc-timeout-remaining
Browse files Browse the repository at this point in the history
Pc timeout remaining
  • Loading branch information
mary-dcouto authored Jul 26, 2021
2 parents f84befe + 58f036d commit 90367f6
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 56 deletions.
106 changes: 82 additions & 24 deletions service/cziti/sdk.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import (
"os"
"strings"
"sync"
"sync/atomic"
"time"
"unsafe"
)
Expand Down Expand Up @@ -82,17 +83,19 @@ type BulkServiceChange struct {
ServicesToAdd []*dto.Service
MfaMinTimeout int32
MfaMaxTimeout int32
MfaMinTimeoutRem int32
MfaMaxTimeoutRem int32
MfaLastUpdatedTime time.Time
}

type NotificationMessage struct {
IdentityName string
Fingerprint string
Message string
IdentityName string
Fingerprint string
Message string
MfaMinimumTimeout int32
MfaMaximumTimeout int32
MfaTimeDuration int
Severity string
Severity string
}

type TunnelNotificationEvent struct {
Expand Down Expand Up @@ -156,6 +159,8 @@ type ZIdentity struct {
MfaEnabled bool
MfaMinTimeout int32
MfaMaxTimeout int32
MfaMinTimeoutRem int32
MfaMaxTimeoutRem int32
MfaLastUpdatedTime time.Time
}

Expand All @@ -164,8 +169,10 @@ func NewZid(statusChange func(int)) *ZIdentity {
zid.Services = sync.Map{}
zid.Options = (*C.ziti_options)(C.calloc(1, C.sizeof_ziti_options))
zid.StatusChanges = statusChange
zid.MfaMinTimeout = -1
zid.MfaMinTimeoutRem = -1
zid.MfaMaxTimeoutRem = -1
zid.MfaMaxTimeout = -1
zid.MfaMinTimeout = -1
zid.MfaLastUpdatedTime = time.Now()
return zid
}
Expand Down Expand Up @@ -247,7 +254,7 @@ func (zid *ZIdentity) Shutdown() {
}

func (zid *ZIdentity) MfaRefreshNeeded() bool {
return !(zid.MfaMinTimeout == -1 && zid.MfaMaxTimeout == -1)
return !(zid.MfaMinTimeoutRem == -1 && zid.MfaMaxTimeoutRem == -1)
}

func (zid *ZIdentity) GetRemainingTime(timeout int32) int32 {
Expand All @@ -262,18 +269,43 @@ func (zid *ZIdentity) GetRemainingTime(timeout int32) int32 {

func (zid *ZIdentity) GetMFAState(interval int32) int {
var mfaState int
if (zid.MfaMaxTimeout > -1) && (zid.MfaMaxTimeout-int32(time.Since(zid.MfaLastUpdatedTime).Seconds())) < 0 {
if (zid.MfaMaxTimeoutRem > -1) && (zid.MfaMaxTimeoutRem-int32(time.Since(zid.MfaLastUpdatedTime).Seconds())) < 0 {
mfaState = constants.MfaAllSvcTimeout
} else if (zid.MfaMinTimeout - int32(time.Since(zid.MfaLastUpdatedTime).Seconds())) < 0 {
} else if (zid.MfaMinTimeoutRem - int32(time.Since(zid.MfaLastUpdatedTime).Seconds())) < 0 {
mfaState = constants.MfaFewSvcTimeout
} else if (zid.MfaMinTimeout - int32(time.Since(zid.MfaLastUpdatedTime).Seconds())) < interval {
} else if (zid.MfaMinTimeoutRem - int32(time.Since(zid.MfaLastUpdatedTime).Seconds())) < interval {
mfaState = constants.MfaNearingTimeout
} else {
mfaState = -1
}
return mfaState
}

func (zid *ZIdentity) UpdateMFATime() {

zid.Services.Range(func(key interface{}, value interface{}) bool {
//string, ZService
val := value.(*ZService)

if zid.MfaRefreshNeeded() && val.Service.Timeout > 0 {
var svcTimeout int32 = -1
for _, pc := range val.Service.PostureChecks {
if svcTimeout == -1 || svcTimeout > int32(pc.Timeout) {
svcTimeout = int32(pc.Timeout)
}
break
}
atomic.StoreInt32(&val.Service.TimeoutRemaining, svcTimeout)
}

return true
})
zid.MfaLastUpdatedTime = time.Now()
zid.MfaMaxTimeoutRem = zid.MfaMaxTimeout
zid.MfaMinTimeoutRem = zid.MfaMinTimeout
log.Debugf("updated Mfa Maximum Timeout Remaining to %d and Mfa Minimum Timeout Remaining to %d", zid.MfaMaxTimeoutRem, zid.MfaMinTimeoutRem)
}

//export doZitiShutdown
func doZitiShutdown(async *C.uv_async_t) {
ctx := C.ziti_context(async.data)
Expand Down Expand Up @@ -366,6 +398,7 @@ func serviceCB(ziti_ctx C.ziti_context, service *C.ziti_service, status C.int, z
//if any posture query sets pass - that will grant the user access to that service
hasAccess := false
timeout := -1
timeoutRemaining := -1

//find all posture checks sets...
for setIdx := 0; true; setIdx++ {
Expand Down Expand Up @@ -395,7 +428,7 @@ func serviceCB(ziti_ctx C.ziti_context, service *C.ziti_service, status C.int, z

var pcId string
pcId = C.GoString(pq.id)
log.Infof("Posture query %s, timeout %d", C.GoString(pq.id), int(pq.timeout))
log.Infof("Posture query %s, timeout %d, timeoutRemaining %d", C.GoString(pq.id), int(pq.timeout), int(pq.timeoutRemaining))

_, found := pcIds[pcId]
if found {
Expand All @@ -405,34 +438,40 @@ func serviceCB(ziti_ctx C.ziti_context, service *C.ziti_service, status C.int, z
if timeout == -1 || timeout > int(pq.timeout) {
timeout = int(pq.timeout)
}
if timeoutRemaining == -1 || timeoutRemaining > int(pq.timeoutRemaining) {
timeoutRemaining = int(pq.timeoutRemaining)
}
break
}
}
} else {
pcIds[C.GoString(pq.id)] = false
pc := dto.PostureCheck{
IsPassing: bool(pq.is_passing),
QueryType: C.GoString(pq.query_type),
Id: pcId,
Timeout: int(pq.timeout),
IsPassing: bool(pq.is_passing),
QueryType: C.GoString(pq.query_type),
Id: pcId,
Timeout: int(pq.timeout),
TimeoutRemaining: int(pq.timeoutRemaining),
}
timeout = pc.Timeout
timeoutRemaining = pc.TimeoutRemaining

postureChecks = append(postureChecks, pc)
}
}
}

svc = &dto.Service{
Name: name,
Id: svcId,
Protocols: protocols,
Addresses: addresses,
Ports: portRanges,
OwnsIntercept: true,
PostureChecks: postureChecks,
IsAccessable: hasAccess,
Timeout: int32(timeout),
Name: name,
Id: svcId,
Protocols: protocols,
Addresses: addresses,
Ports: portRanges,
OwnsIntercept: true,
PostureChecks: postureChecks,
IsAccessable: hasAccess,
Timeout: int32(timeout),
TimeoutRemaining: int32(timeoutRemaining),
}
added := ZService{
Name: name,
Expand Down Expand Up @@ -584,6 +623,8 @@ func eventCB(ztx C.ziti_context, event *C.ziti_event_t) {
}
var minimumTimeout int32 = -1
var maximumTimeout int32 = -1
var minimumTimeoutRem int32 = -1
var maximumTimeoutRem int32 = -1
noTimeoutSvc := false
if len(servicesToAdd) > 0 {
zid.Services.Range(func(key interface{}, value interface{}) bool {
Expand All @@ -600,17 +641,32 @@ func eventCB(ztx C.ziti_context, event *C.ziti_event_t) {
} else {
noTimeoutSvc = true
}
if val.Service.TimeoutRemaining >= 0 {
if minimumTimeoutRem == -1 || minimumTimeoutRem > val.Service.TimeoutRemaining {
minimumTimeoutRem = val.Service.TimeoutRemaining
}
if maximumTimeoutRem == -1 || maximumTimeoutRem < val.Service.TimeoutRemaining {
maximumTimeoutRem = val.Service.TimeoutRemaining
}
} else {
noTimeoutSvc = true
}

return true

})
if noTimeoutSvc {
maximumTimeout = -1
maximumTimeoutRem = -1
}
}

now := time.Now()
zid.MfaMinTimeout = minimumTimeout
zid.MfaMaxTimeout = maximumTimeout
zid.MfaMinTimeoutRem = minimumTimeoutRem
zid.MfaMaxTimeoutRem = maximumTimeoutRem
zid.MfaLastUpdatedTime = now

svcChange := BulkServiceChange{
Fingerprint: zid.Fingerprint,
Expand All @@ -620,7 +676,9 @@ func eventCB(ztx C.ziti_context, event *C.ziti_event_t) {
ServicesToRemove: servicesToRemove,
MfaMinTimeout: minimumTimeout,
MfaMaxTimeout: maximumTimeout,
MfaLastUpdatedTime: time.Now(),
MfaMinTimeoutRem: minimumTimeoutRem,
MfaMaxTimeoutRem: maximumTimeoutRem,
MfaLastUpdatedTime: now,
}

if len(BulkServiceChanges) == cap(BulkServiceChanges) {
Expand Down
4 changes: 2 additions & 2 deletions service/ziti-tunnel/constants/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const (
MinimumFrequency = 5 // minimum notification window
MaximumFrequency = 20 // maximum notification window

MfaAllSvcTimeout = 0
MfaFewSvcTimeout = 1
MfaAllSvcTimeout = 0
MfaFewSvcTimeout = 1
MfaNearingTimeout = 2
)
30 changes: 17 additions & 13 deletions service/ziti-tunnel/dto/dtos.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,16 @@ type AddIdentity struct {
}

type Service struct {
Name string
Id string
Protocols []string
Addresses []Address
Ports []PortRange
OwnsIntercept bool
PostureChecks []PostureCheck
IsAccessable bool
Timeout int32
Name string
Id string
Protocols []string
Addresses []Address
Ports []PortRange
OwnsIntercept bool
PostureChecks []PostureCheck
IsAccessable bool
Timeout int32
TimeoutRemaining int32
}

type Address struct {
Expand All @@ -56,10 +57,11 @@ type PortRange struct {
}

type PostureCheck struct {
IsPassing bool
QueryType string
Id string
Timeout int
IsPassing bool
QueryType string
Id string
Timeout int
TimeoutRemaining int
}

type ServiceOwner struct {
Expand All @@ -84,6 +86,8 @@ type Identity struct {
Tags []string `json:",omitempty"`
MfaMinTimeout int32
MfaMaxTimeout int32
MfaMinTimeoutRem int32
MfaMaxTimeoutRem int32
MfaLastUpdatedTime time.Time
}
type Metrics struct {
Expand Down
35 changes: 18 additions & 17 deletions service/ziti-tunnel/service/ipc.go
Original file line number Diff line number Diff line change
Expand Up @@ -1196,31 +1196,31 @@ func handleEvents(isInitialized chan struct{}) {

var notificationMinTimeout int32 = 0
var notificationMaxTimeout int32 = -1
switch mfaState := id.CId.GetMFAState(int32((constants.MaximumFrequency+rts.state.NotificationFrequency)*60)); mfaState {
switch mfaState := id.CId.GetMFAState(int32((constants.MaximumFrequency + rts.state.NotificationFrequency) * 60)); mfaState {
case constants.MfaAllSvcTimeout:
notificationMessage = fmt.Sprintf("All of the services of identity %s are timed out", id.Name)
case constants.MfaFewSvcTimeout:
notificationMessage = fmt.Sprintf("Some of the services of identity %s are timed out", id.Name)
case constants.MfaNearingTimeout:
notificationMinTimeout = id.CId.GetRemainingTime(id.CId.MfaMinTimeout)
notificationMinTimeout = id.CId.GetRemainingTime(id.CId.MfaMinTimeoutRem)
notificationMessage = fmt.Sprintf("Some of the services of identity %s are timing out in %s", id.Name, secondsToReadableFmt(notificationMinTimeout))
default:
// do nothing
}
if len(notificationMessage) > 0 {

if id.CId.MfaMaxTimeout > -1 {
notificationMaxTimeout = id.CId.GetRemainingTime(id.CId.MfaMaxTimeout)
if id.CId.MfaMaxTimeoutRem > -1 {
notificationMaxTimeout = id.CId.GetRemainingTime(id.CId.MfaMaxTimeoutRem)
}
notificationMinTimeout = id.CId.GetRemainingTime(id.CId.MfaMinTimeout)
notificationMinTimeout = id.CId.GetRemainingTime(id.CId.MfaMinTimeoutRem)

cleanNotifications = append(cleanNotifications, cziti.NotificationMessage{
Fingerprint: id.FingerPrint,
IdentityName: id.Name,
Severity: "major",
Fingerprint: id.FingerPrint,
IdentityName: id.Name,
Severity: "major",
MfaMinimumTimeout: notificationMinTimeout,
MfaMaximumTimeout: notificationMaxTimeout,
Message: notificationMessage,
Message: notificationMessage,
MfaTimeDuration: int(time.Since(id.CId.MfaLastUpdatedTime).Seconds()),
})
}
Expand Down Expand Up @@ -1272,18 +1272,18 @@ func Clean(src *Id) dto.Identity {
//string, ZService
val := value.(*cziti.ZService)

if src.CId.MfaRefreshNeeded() && val.Service.Timeout > 0 {
if src.CId.MfaRefreshNeeded() && val.Service.TimeoutRemaining > 0 {
var svcTimeout int32 = -1
for _, pc := range val.Service.PostureChecks {
if svcTimeout == -1 || svcTimeout > int32(pc.Timeout) {
svcTimeout = int32(pc.Timeout)
if svcTimeout == -1 || svcTimeout > int32(pc.TimeoutRemaining) {
svcTimeout = int32(pc.TimeoutRemaining)
}
break
}
if (svcTimeout - int32(time.Since(src.CId.MfaLastUpdatedTime).Seconds())) < 0 {
atomic.StoreInt32(&val.Service.Timeout, 0)
atomic.StoreInt32(&val.Service.TimeoutRemaining, 0)
} else {
atomic.StoreInt32(&val.Service.Timeout, svcTimeout-int32(time.Since(src.CId.MfaLastUpdatedTime).Seconds()))
atomic.StoreInt32(&val.Service.TimeoutRemaining, svcTimeout-int32(time.Since(src.CId.MfaLastUpdatedTime).Seconds()))
}
}

Expand All @@ -1292,11 +1292,13 @@ func Clean(src *Id) dto.Identity {
})
nid.MfaMinTimeout = src.CId.MfaMinTimeout
nid.MfaMaxTimeout = src.CId.MfaMaxTimeout
nid.MfaMinTimeoutRem = src.CId.MfaMinTimeoutRem
nid.MfaMaxTimeoutRem = src.CId.MfaMaxTimeoutRem
if !src.CId.MfaRefreshNeeded() {
nid.MfaLastUpdatedTime = time.Now()
} else {
nid.MfaLastUpdatedTime = src.CId.MfaLastUpdatedTime
if (nid.MfaMaxTimeout-int32(time.Since(nid.MfaLastUpdatedTime).Seconds())) < 0 && nid.MfaEnabled {
if (nid.MfaMaxTimeoutRem-int32(time.Since(nid.MfaLastUpdatedTime).Seconds())) < 0 && nid.MfaEnabled {
nid.MfaNeeded = true
}
}
Expand All @@ -1322,8 +1324,7 @@ func authMfa(out *json.Encoder, fingerprint string, code string) {
id := rts.Find(fingerprint)
result := cziti.AuthMFA(id.CId, code)
if result == nil {
id.CId.MfaNeeded = false
id.CId.MfaLastUpdatedTime = time.Now()
id.CId.UpdateMFATime()
respond(out, dto.Response{Message: "AuthMFA complete", Code: SUCCESS, Error: "", Payload: fingerprint})
} else {
respondWithError(out, fmt.Sprintf("AuthMFA failed. the supplied code [%s] was not valid: %s", code, result), 1, result)
Expand Down
2 changes: 2 additions & 0 deletions service/ziti-tunnel/service/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ func (t *RuntimeState) ToMetrics() dto.TunnelStatus {
MfaNeeded: id.MfaNeeded,
MfaMinTimeout: id.MfaMinTimeout,
MfaMaxTimeout: id.MfaMaxTimeout,
MfaMinTimeoutRem: id.MfaMinTimeoutRem,
MfaMaxTimeoutRem: id.MfaMaxTimeoutRem,
MfaLastUpdatedTime: id.MfaLastUpdatedTime,
}
i++
Expand Down

0 comments on commit 90367f6

Please sign in to comment.