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

Feature Request: IsPrivate #561

Closed
henricook opened this issue Feb 5, 2024 · 6 comments · Fixed by #562
Closed

Feature Request: IsPrivate #561

henricook opened this issue Feb 5, 2024 · 6 comments · Fixed by #562

Comments

@henricook
Copy link

henricook commented Feb 5, 2024

Hi ip4s team,

I'm casting around for a Scala/Java library that can parse ipv4 and 6 addresses and tell me whether they're 'internal' i.e. come from a private subnet. I don't think this is possible with ip4s at the moment but it would be a great feature.

Use case: Some endpoints in my application I want to be restricted to internal services only, which can be using a mix of ipv4 and 6. Or an external caller on ipv6 can try and access the endpoint and needs to be rejected.

@mpilquist
Copy link
Member

Sounds like a great addition. Will open a PR today hopefully.

@henricook
Copy link
Author

Thanks for a speedy response @mpilquist - here's how I implemented it in straight Java in case it helps:

    def isValidIPAddress(candidateIP: String): Boolean = {
      ipv4Regex.matches(candidateIP) || ipv6Regex.matches(candidateIP)
    }

    val sourceIP = RealIP.sourceIP(request).value // Proprietary way of grabbing the IP - basically REMOTE_ADDR

    // getByName can perform DNS lookups if we accidentally pass a hostname, so attempt our own validation first
    // to make sure that sourceIP is a valid ipv4 or ipv6 address
    val sourceIPAsInet = isValidIPAddress(sourceIP).toOption.flatMap { _ =>
      Try(InetAddress.getByName(sourceIP)) match {
        case Success(addr) => Some(addr)
        case Failure(ex)   =>
          unsafeLogger.warn(s"Failed to get IP address for ${sourceIP}", ex)
          None
      }
    }

    val sourceIPIsInternal = sourceIPAsInet match {
      case Some(validAddress) =>
        validAddress.isLoopbackAddress || validAddress.isLinkLocalAddress || validAddress.isSiteLocalAddress
      case None               =>
        false // If the address is malformed assume the safest option: That it's not internal
    }

@mpilquist
Copy link
Member

Take a look at #562. As of now, it's only returning true for addresses in v4 A/B/C private ranges and v6 private range.

@henricook
Copy link
Author

Pretty sweet. A loopback option would be nice for my use case (because we run this software locally and want to call these endpoints) but I appreciate that could be a separate option

@mpilquist
Copy link
Member

Yep, your example convinced me to add isLoopback and isLinkLocal as well. Just pushed another commit.

@henricook
Copy link
Author

henricook commented Feb 12, 2024

@mpilquist Some additional trivia for you on this one. ChatGPT recommended ip4s for this task but said use a .isPrivate method that didn't exist. So by implementing this method, you've actually error-corrected ChatGPT in a roundabout way! :-)

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 a pull request may close this issue.

2 participants