-
Notifications
You must be signed in to change notification settings - Fork 176
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Teleport: Update Rules * Update global_helpers/panther_default.py * Update global_helpers/panther_default.py * Update rules/gravitational_teleport_rules/teleport_long_lived_certs.py Co-authored-by: Ariel Ropek <[email protected]> * Update rules/gravitational_teleport_rules/teleport_long_lived_certs.py Co-authored-by: Ariel Ropek <[email protected]> * Update rules/gravitational_teleport_rules/teleport_long_lived_certs.py Co-authored-by: Ariel Ropek <[email protected]> * Update rules/gravitational_teleport_rules/teleport_long_lived_certs.py * Update rules/gravitational_teleport_rules/teleport_long_lived_certs.py * Update rules/gravitational_teleport_rules/teleport_long_lived_certs.py * Update rules/gravitational_teleport_rules/teleport_long_lived_certs.py appease fmt * Update rules/gravitational_teleport_rules/teleport_long_lived_certs.py --------- Co-authored-by: Ariel Ropek <[email protected]>
- Loading branch information
1 parent
6252b95
commit 1f8841b
Showing
23 changed files
with
511 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
rules/gravitational_teleport_rules/teleport_local_user_login_without_mfa.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
SENSITIVE_LOCAL_USERS = ["breakglass"] | ||
|
||
|
||
def rule(event): | ||
return ( | ||
event.get("event") == "user.login" | ||
and event.get("success") == "true" | ||
and event.get("method") == "local" | ||
and not event.get("mfa_device") | ||
) | ||
|
||
|
||
def severity(event): | ||
if event.get("user") in SENSITIVE_LOCAL_USERS: | ||
return "HIGH" | ||
return "MEDIUM" | ||
|
||
|
||
def title(event): | ||
return ( | ||
f"User [{event.get('user', '<UNKNOWN_USER>')}] logged into " | ||
f"[{event.get('cluster_name', '<UNNAMED_CLUSTER>')}] locally " | ||
f"without using MFA" | ||
) |
64 changes: 64 additions & 0 deletions
64
rules/gravitational_teleport_rules/teleport_local_user_login_without_mfa.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
AnalysisType: rule | ||
Filename: teleport_local_user_login_without_mfa.py | ||
RuleID: Teleport.LocalUserLoginWithoutMFA | ||
DisplayName: User Logged in wihout MFA | ||
Enabled: true | ||
LogTypes: | ||
- Gravitational.TeleportAudit | ||
Tags: | ||
- Teleport | ||
Severity: High | ||
Description: A local User logged in without MFA | ||
DedupPeriodMinutes: 60 | ||
Reports: | ||
MITRE ATT&CK: | ||
- TA0001:T1078 | ||
Reference: https://goteleport.com/docs/management/admin/ | ||
Runbook: > | ||
A local user logged in without Multi-Factor Authentication | ||
SummaryAttributes: | ||
- event | ||
- code | ||
- user | ||
- success | ||
- mfa_device | ||
Tests: | ||
- | ||
Name: User logged in with MFA | ||
ExpectedResult: false | ||
Log: | ||
{ | ||
"addr.remote": "[2001:db8:feed:face:c0ff:eeb0:baf00:00d]:65123", | ||
"cluster_name": "teleport.example.com", | ||
"code": "T1000I", | ||
"ei": 0, | ||
"event": "user.login", | ||
"method": "local", | ||
"mfa_device": { | ||
"mfa_device_name": "1Password", | ||
"mfa_device_type": "WebAuthn", | ||
"mfa_device_uuid": "88888888-4444-4444-4444-222222222222" | ||
}, | ||
"success": true, | ||
"time": "2023-09-20T19:00:00.123456Z", | ||
"uid": "88888888-4444-4444-4444-222222222222", | ||
"user": "max.mustermann", | ||
"user_agent": "Examplecorp Spacedeck-web/99.9 (Hackintosh; ARM Cortex A1000)" | ||
} | ||
- | ||
Name: User logged in without MFA | ||
ExpectedResult: false | ||
Log: | ||
{ | ||
"addr.remote": "[2001:db8:face:face:face:face:face:face]:65123", | ||
"cluster_name": "teleport.example.com", | ||
"code": "T1000I", | ||
"ei": 0, | ||
"event": "user.login", | ||
"method": "local", | ||
"success": true, | ||
"time": "2023-09-20T19:00:00.123456Z", | ||
"uid": "88888888-4444-4444-4444-222222222222", | ||
"user": "max.mustermann", | ||
"user_agent": "Examplecorp Spacedeck-web/99.9 (Hackintosh; ARM Cortex A1000)" | ||
} |
10 changes: 10 additions & 0 deletions
10
rules/gravitational_teleport_rules/teleport_lock_created.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
def rule(event): | ||
return event.get("event") == "lock.created" | ||
|
||
|
||
def title(event): | ||
return ( | ||
f"A Teleport Lock was created by {event.get('updated_by', '<UNKNOWN_UPDATED_BY>')} " | ||
f"to Lock out user {event.get('target', {}).get('user', '<UNKNOWN_USER>')} " | ||
f"on [{event.get('cluster_name', '<UNKNOWN_CLUSTER>')}]" | ||
) |
40 changes: 40 additions & 0 deletions
40
rules/gravitational_teleport_rules/teleport_lock_created.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
AnalysisType: rule | ||
Filename: teleport_lock_created.py | ||
RuleID: Teleport.LockCreated | ||
DisplayName: A Teleport Lock was created | ||
Enabled: true | ||
LogTypes: | ||
- Gravitational.TeleportAudit | ||
Tags: | ||
- Teleport | ||
Severity: Info | ||
Description: A Teleport Lock was created | ||
DedupPeriodMinutes: 60 | ||
Reference: https://goteleport.com/docs/management/admin/ | ||
Runbook: > | ||
A Teleport Lock was created; this is an unusual administrative action. Investigate to understand why a Lock was created. | ||
SummaryAttributes: | ||
- event | ||
- code | ||
- time | ||
- identity | ||
Tests: | ||
- | ||
Name: A Lock was created | ||
ExpectedResult: true | ||
Log: | ||
{ | ||
"cluster_name": "teleport.example.com", | ||
"code": "TLK00I", | ||
"ei": 0, | ||
"event": "lock.created", | ||
"expires": "0001-01-01T00:00:00Z", | ||
"name": "88888888-4444-4444-4444-222222222222", | ||
"target": { | ||
"user": "user-to-disable" | ||
}, | ||
"time": "2023-09-21T00:00:00.000000Z", | ||
"uid": "88888888-4444-4444-4444-222222222222", | ||
"updated_by": "[email protected]", | ||
"user": "[email protected]" | ||
} |
79 changes: 79 additions & 0 deletions
79
rules/gravitational_teleport_rules/teleport_long_lived_certs.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
from datetime import timedelta, datetime | ||
from typing import Dict, Tuple | ||
|
||
from panther_base_helpers import ( | ||
golang_nanotime_to_python_datetime, | ||
panther_nanotime_to_python_datetime, | ||
) | ||
|
||
PANTHER_TIME_FORMAT = r"%Y-%m-%d %H:%M:%S.%f" | ||
# Tune this to be some Greatest Common Denominator of session TTLs for your | ||
# environment | ||
MAXIMUM_NORMAL_VALIDITY_INTERVAL = timedelta(hours=12) | ||
# To allow some time in between when a request is submitted and authorized | ||
# vs when the certificate actually gets generated. In practice, this is much | ||
# less than 5 seconds. | ||
ISSUANCE_GRACE_PERIOD = timedelta(seconds=5) | ||
|
||
# You can audit your logs in Panther to try and understand your role/validity | ||
# patterns from a known-good period of access. | ||
# A query example: | ||
# ```sql | ||
# SELECT | ||
# cluster_name, | ||
# identity:roles, | ||
# DATEDIFF('HOUR', time, identity:expires) AS validity | ||
# FROM | ||
# panther_logs.public.gravitational_teleportaudit | ||
# WHERE | ||
# p_occurs_between('2023-09-01 00:00:00','2023-10-06 21:00:00Z') | ||
# AND event = 'cert.create' | ||
# GROUP BY cluster_name, identity:roles, validity | ||
# ORDER BY validity DESC | ||
# ``` | ||
|
||
# A dictionary of: | ||
# cluster names: to a dictionary of: | ||
# role names: mapping to a tuple of: | ||
# ( maximum usual validity, expiration datetime for this rule ) | ||
CLUSTER_ROLE_MAX_VALIDITIES: Dict[str, Dict[str, Tuple[timedelta, datetime]]] = { | ||
# "teleport.example.com": { | ||
# "example_role": (timedelta(hours=720), datetime(2023, 12, 01, 01, 02, 03)), | ||
# "other_example_role": (timedelta(hours=720), datetime.max), | ||
# }, | ||
} | ||
|
||
|
||
def rule(event): | ||
if not event.get("event") == "cert.create": | ||
return False | ||
max_validity = MAXIMUM_NORMAL_VALIDITY_INTERVAL + ISSUANCE_GRACE_PERIOD | ||
for role in event.deep_get("identity", "roles", default=[]): | ||
validity, expiration = CLUSTER_ROLE_MAX_VALIDITIES.get(event.get("cluster_name"), {}).get( | ||
role, (None, None) | ||
) | ||
if validity and expiration: | ||
# Ignore exceptions that have passed their expiry date | ||
if datetime.utcnow() < expiration: | ||
max_validity = max(max_validity, validity) | ||
return validity_interval(event) > max_validity | ||
|
||
|
||
def validity_interval(event): | ||
event_time = panther_nanotime_to_python_datetime(event.get("time")) | ||
expires = golang_nanotime_to_python_datetime( | ||
event.deep_get("identity", "expires", default=None) | ||
) | ||
if not event_time and expires: | ||
return False | ||
interval = expires - event_time | ||
return interval | ||
|
||
|
||
def title(event): | ||
identity = event.deep_get("identity", "user", default="<Cert with no User!?>") | ||
return ( | ||
f"A Certificate for [{identity}] " | ||
f"on [{event.get('cluster_name', '<UNKNOWN_CLUSTER>')}] " | ||
f"has been issued for an unusually long time: {validity_interval(event)!r} " | ||
) |
92 changes: 92 additions & 0 deletions
92
rules/gravitational_teleport_rules/teleport_long_lived_certs.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
AnalysisType: rule | ||
Filename: teleport_long_lived_certs.py | ||
RuleID: Teleport.LongLivedCerts | ||
DisplayName: A long-lived cert was created | ||
Enabled: true | ||
LogTypes: | ||
- Gravitational.TeleportAudit | ||
Tags: | ||
- Teleport | ||
Severity: Medium | ||
Description: An unusually long-lived Teleport certificate was created | ||
DedupPeriodMinutes: 60 | ||
Reports: | ||
MITRE ATT&CK: | ||
- TA0003:T1098 | ||
Reference: https://goteleport.com/docs/management/admin/ | ||
Runbook: > | ||
Teleport certificates are usually issued for a short period of time. Alert if long-lived certificates were created. | ||
SummaryAttributes: | ||
- event | ||
- code | ||
- time | ||
- identity | ||
Tests: | ||
- | ||
Name: A certificate was created for the default period of 1 hour | ||
ExpectedResult: false | ||
Log: | ||
{ | ||
"cert_type": "user", | ||
"cluster_name": "teleport.example.com", | ||
"code": "TC000I", | ||
"ei": 0, | ||
"event": "cert.create", | ||
"time": "2023-09-17 21:00:00.000000", | ||
"identity": { | ||
"disallow_reissue": true, | ||
"expires": "2023-09-17T22:00:00.444444428Z", | ||
"impersonator": "bot-application", | ||
"kubernetes_cluster": "staging", | ||
"kubernetes_groups": [ | ||
"application" | ||
], | ||
"logins": [ | ||
"-teleport-nologin-88888888-4444-4444-4444-222222222222", | ||
"-teleport-internal-join" | ||
], | ||
"prev_identity_expires": "0001-01-01T00:00:00Z", | ||
"roles": [ | ||
"application" | ||
], | ||
"route_to_cluster": "teleport.example.com", | ||
"teleport_cluster": "teleport.example.com", | ||
"traits": {}, | ||
"user": "bot-application" | ||
}, | ||
"uid": "88888888-4444-4444-4444-222222222222" | ||
} | ||
- | ||
Name: A certificate was created for longer than the default period of 1 hour | ||
ExpectedResult: true | ||
Log: | ||
{ | ||
"cert_type": "user", | ||
"cluster_name": "teleport.example.com", | ||
"code": "TC000I", | ||
"ei": 0, | ||
"event": "cert.create", | ||
"time": "2023-09-17 21:00:00.000000", | ||
"identity": { | ||
"disallow_reissue": true, | ||
"expires": "2043-09-17T22:00:00.444444428Z", | ||
"impersonator": "bot-application", | ||
"kubernetes_cluster": "staging", | ||
"kubernetes_groups": [ | ||
"application" | ||
], | ||
"logins": [ | ||
"-teleport-nologin-88888888-4444-4444-4444-222222222222", | ||
"-teleport-internal-join" | ||
], | ||
"prev_identity_expires": "0001-01-01T00:00:00Z", | ||
"roles": [ | ||
"application" | ||
], | ||
"route_to_cluster": "teleport.example.com", | ||
"teleport_cluster": "teleport.example.com", | ||
"traits": {}, | ||
"user": "bot-application" | ||
}, | ||
"uid": "88888888-4444-4444-4444-222222222222" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.