Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RUMM-2153 Prevent Kronos logic from querying private IPs #830

Merged

Conversation

ncreated
Copy link
Collaborator

@ncreated ncreated commented Apr 27, 2022

What and why?

🐞 This PR aims at fixing #647. As explained in #647 (comment) we found an evidence (in our internal telemetry) that in some circumstances Kronos (NTP) logic can try to query private IP and hence bring up the "Local Network Permission" alert.

This happens very rarely, and except a single hit in our logs we couldn't manage to reproduce it.

How?

Our telemetry shows a clear attempt on querying 192.168.1.250. To avoid this, in this PR we apply additional filtering in KronosDNSResolver to ensure that private IPs are never used for NTP sync.

165082340-1c4f5a8a-219e-4ea7-81ae-a07fb5d5f976

If all resolved IPs are filtered out, Kronos will fail silently and fallback to previous known time correction (stored in user defaults). In case of fresh start, the SDK falls back to using device time:

[DATADOG SDK] 🐢 β†’ 11:45:15.385 [WARN] NTP time synchronization failed.
Device time will be used for signing events (current device time is 2022-04-27 09:45:15 +0000).

Review checklist

  • Feature or bugfix MUST have appropriate tests (unit, integration)
  • Make sure each commit and the PR mention the Issue number or JIRA reference
  • Add CHANGELOG entry for user facing changes

Custom CI job configuration (optional)

  • Run unit tests
  • Run integration tests
  • Run smoke tests

@ncreated ncreated force-pushed the ncreated/RUMM-2153-prevent-Kronos-from-querying-local-IPs branch from 0ae9c40 to 762292f Compare April 27, 2022 09:53
@ncreated ncreated self-assigned this Apr 27, 2022
Comment on lines 66 to 93
static func mockIPv4(_ bytes: [UInt8]) throws -> KronosInternetAddress {
precondition(bytes.count == 4, "Expected 4 bytes")
let numbers = bytes.map { String($0) }
let ipv4String = numbers.joined(separator: ".") // e.g. '192.168.1.1'
let address: KronosInternetAddress? = .mockWith(ipv4String: ipv4String)
return try XCTUnwrap(address, "\(ipv4String) is not a valid IPv4 string")
}

static func mockIPv6(_ bytes: [UInt8]) throws -> KronosInternetAddress {
precondition(bytes.count == 16, "Expected 16 bytes")
let groups: [String] = (0..<8).map { idx in
let hexA = String(bytes[idx * 2], radix: 16, uppercase: false)
let hexB = String(bytes[idx * 2 + 1], radix: 16, uppercase: false)
return hexA + hexB
}
let ipv6String = groups.joined(separator: ":") // e.g. 'ab:ab:ab:ab:ab:ab:ab:ab'
let address: KronosInternetAddress? = .mockWith(ipv6String: ipv6String)
return try XCTUnwrap(address, "\(ipv6String) is not a valid IPv6 string")
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way we mock KronosInternetAddress values for IPv4 and IPv6 is quite tricky, but couldn't find a way to make it simpler. Alternatively, we could implement filtering as stand-alone function (isPrivate(host:)), but I found computed property (ip.isPrivate) more idiomatic.

Copy link
Member

@maxep maxep Apr 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure of the fuzzy tests value here πŸ€” especially since the generated IPs is valid/expected. We could simply test against edges and some pre-determined values in between, no?

Copy link
Collaborator Author

@ncreated ncreated Apr 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we're not checking against invalid input because in practise it is impossible to receive one (IRL the IP is read by CFHostGetAddressing() and bound to arbitrary memory, so invalid input would rather cause a crash in system frameworks than be allowed).

On the practical side, having 100% coverage among all allowed inputs makes it easier to change this classification logic. For that reason I'd rather keep it as it is.

@ncreated ncreated marked this pull request as ready for review April 27, 2022 10:11
@ncreated ncreated requested a review from a team as a code owner April 27, 2022 10:11
@buranmert buranmert changed the title RUMM-2153 Prevent Kronos logic from querying privte IPs RUMM-2153 Prevent Kronos logic from querying private IPs Apr 27, 2022
Copy link
Contributor

@buranmert buranmert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i left a somewhat minor comment but given the importance of this issue i'm requsting a change

Sources/Datadog/Kronos/KronosInternetAddress.swift Outdated Show resolved Hide resolved
@ncreated ncreated force-pushed the ncreated/RUMM-2153-prevent-Kronos-from-querying-local-IPs branch from 762292f to 25d9eae Compare April 28, 2022 09:29
@ncreated ncreated requested a review from buranmert April 28, 2022 09:31
@buranmert
Copy link
Contributor

we can mention our fix in Kronos repo (MobileNativeFoundation/Kronos#94) so that other users can apply a similar fix

@ncreated ncreated merged commit b6a1b61 into develop Apr 28, 2022
@ncreated ncreated deleted the ncreated/RUMM-2153-prevent-Kronos-from-querying-local-IPs branch April 28, 2022 10:17
jeannustre pushed a commit to jeannustre/Kronos that referenced this pull request Aug 22, 2022
Changes from DataDog/dd-sdk-ios#830
Also added 'GENERATE_INFOPLIST_FILE = YES;' to Tests target in order for tests to run.
jeannustre pushed a commit to jeannustre/Kronos that referenced this pull request Aug 22, 2022
Changes from DataDog/dd-sdk-ios#830
Also added 'GENERATE_INFOPLIST_FILE = YES;' to Tests target in order for tests to run.

Signed-off-by: jean <jean@witick.io>
@pepas-everly
Copy link

We have recently been bitten by this. Is there an easy way to disable NTP altogether? The device clocks are already more than accurate enough for our needs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants