Skip to content

feat(aws): restrict security group inbound to current user IP by default#362

Open
alexjurkiewicz wants to merge 4 commits into
PierreBeucher:masterfrom
alexjurkiewicz:feat/aws-sg-restrict-to-my-ip
Open

feat(aws): restrict security group inbound to current user IP by default#362
alexjurkiewicz wants to merge 4 commits into
PierreBeucher:masterfrom
alexjurkiewicz:feat/aws-sg-restrict-to-my-ip

Conversation

@alexjurkiewicz
Copy link
Copy Markdown
Contributor

@alexjurkiewicz alexjurkiewicz commented Apr 3, 2026

Summary

  • Adds --no-restrict-to-my-ip CLI flag and matching interactive prompt
  • When enabled (the default), detects the user's current IPv4/IPv6 addresses and restricts security group ingress to those CIDRs only
  • IPv4 is required; IPv6 is optional (skipped if no external IPv6 detected)

Details

Previously all inbound ports were open to 0.0.0.0/0 and ::/0. This change closes the security group to the user's current IPs by default, reducing the attack surface on the instance.

IP detection uses a request to checkip.global.api.aws with a 5-second timeout per address family. The resulting /32 and /128 CIDRs are passed to Pulumi and used as the security group ingress CIDRs instead of the open defaults.

The detected IPs are printed to the console so the user can verify them.

Use --no-restrict-to-my-ip to restore the previous open-to-all behaviour (e.g. if using a VPN or dynamic IP).

Test plan

  • Create an instance and confirm the SG inbound rules show your IP /32 (and /128 if you have IPv6)
  • Create an instance with --no-restrict-to-my-ip and confirm SG uses 0.0.0.0/0 / ::/0
  • Confirm detected IPs are printed during provisioning

🤖 Generated with Claude Code

@alexjurkiewicz
Copy link
Copy Markdown
Contributor Author

Note: most Docker setups don't pass IPv6 connectivity to containers, run the script directly on your host OS to set up IPv6 connectivity.

@PierreBeucher
Copy link
Copy Markdown
Owner

Great addition - a long time need for security. Note on implementation and interface: code in Core is also used on the platform, in this context "my IP" does not make sense. Core should remain as agnostic as possible from runtime context (the CLI or another usage), eg:

  • State should remain generic with data like allowedCidrs: [...]
  • If whitelisted CIDR(s) is set, it should be passed as input as high as possible. In our case, it should not be computed/identified at aws/provisioner.ts, but rather during init by prompt user and/or during instance startup
    • I think a good place to fetch "current user public ip" is during initialization prompt (unless given as cli arg)
    • Since it relies on a public API call, we should gracefully handle failure and show a warning such as "can't fetch your current IP" and act accordingly
  • I'm not a fan of having it set by default, though I understand security concern. Lots of user are using their instance from different locations, having this set by default may be dangerous since they'd lose connectivity when moving places.
    • A possibility here is to have a flag to "dynamically" update instance input on start with current IP (it would then update instance state on start and as as when initializing)
    • For input we could have a flag like "autoUpdateCidrWhitelistOnStartWithCurrent" (or something shorter, I'm bad at concise variable names ^^) or even a complex type to manage cidrWhitelist; eg. cidrWhitelist: { cidrs: [ ... ], autoUpdateOnStartWithCurrent: boolean } - that's an idea, to be challenged

Also I have concerns about using an external API for this. How can we trust this domain will remain clean ? What if later someone take control and starts returning their own CIDR to access instances easily?

@alexjurkiewicz
Copy link
Copy Markdown
Contributor Author

Note on implementation and interface: code in Core is also used on the platform, in this context "my IP" does not make sense. Core should remain as agnostic as possible from runtime context (the CLI or another usage),

Understood, I will rework this.

I'm not a fan of having it set by default, though I understand security concern. Lots of user are using their instance from different locations, having this set by default may be dangerous since they'd lose connectivity when moving places.

IMO, the security benefit is critical. Sunshine is not particularly well hardened software. As far as I can see there is absolutely no rate limiting or protection from abuse. Users can easily create a top-1000 password (eg qwerty, password1) which is vulnerable to hacking.

Having start update the SG sounds ideal. I will investigate this approach.

Also I have concerns about using an external API for this. How can we trust this domain will remain clean ? What if later someone take control and starts returning their own CIDR to access instances easily?

The service is run by AWS and hostname is in the .aws TLD. If it's taken over, we have bigger problems 🫠

alexjurkiewicz and others added 4 commits April 5, 2026 07:11
Previously all inbound ports were open to 0.0.0.0/0 and ::/0. This adds
a --no-restrict-to-my-ip flag (and matching interactive prompt) to
control a new restrictToMyIp option, which is enabled by default.

When enabled, the provisioner detects the user's current IPv4 and IPv6
addresses before each Pulumi run by making a request to
checkip.global.api.aws with a 5-second timeout per address family. The
resulting /32 and /128 CIDRs are passed to the Pulumi stack and used as
the security group ingress CIDR instead of the open defaults.

IPv6 is optional: if the user has no external IPv6 address the timeout
fires and IPv6 ingress rules are skipped. IPv4 is required: failure to
detect it raises an error with a hint to use --no-restrict-to-my-ip.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add description field to SimplePortDefinition interface and populate
descriptions for all Sunshine and Wolf ports. Wire description into
AWS security group ingress rule mapping.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ve IPs in CLI layer

- Replace restrictToMyIp boolean flag with allowedCidrs in state schema.
  Defaults to open access (0.0.0.0/0); stores restricted /32+/128 when
  IP restriction is enabled. Refreshed on every provision by the provisioner.
- Move IP detection to CLI layer (resolveAllowedCidrs): fetches current IP
  at create time based on --no-restrict-to-my-ip flag (default: restrict).
- Provisioner re-fetches IPs on each provision so the security group stays
  current across create and start flows. Open CIDRs are passed through as-is.
@alexjurkiewicz alexjurkiewicz force-pushed the feat/aws-sg-restrict-to-my-ip branch from c74b271 to 6e050b4 Compare April 5, 2026 09:25
@alexjurkiewicz
Copy link
Copy Markdown
Contributor Author

Changes:

  • Removed restrictToMyIp: boolean from state. State now stores allowedCidrs: { ipv4, ipv6 } — either open (0.0.0.0/0/::/0) or a specific /32+ /128. Old states without this field default to open access.
  • IP detection moved out of the provisioner and into the CLI layer. The underlying logic is accessible to other provisioners (src/tools/ip.ts)

I'm a little worried I didn't perfectly understand the layers of separation in cloudypad. I'm still wrapping my head around the different ways it works for local users vs SaaS system.

@PierreBeucher
Copy link
Copy Markdown
Owner

PierreBeucher commented Apr 11, 2026

Thanks - that may be a bit complex indeed, you did the boilerplate on IP restriction already. Do you mind if I update your PR directly to make the interface generic and usable on all providers ?

IMO, the security benefit is critical.

Absolutely! Note: for the use case you specifiy, Sunshine UI is not accessible other internet, only streaming ports are open. Accessing UI requires an SSH tunnel (UI access is restricted to LAN by default)

The service is run by AWS and hostname is in the .aws TLD. If it's taken over, we have bigger problems 🫠

Fair enough

@alexjurkiewicz
Copy link
Copy Markdown
Contributor Author

Do you mind if I update your PR directly to make the interface generic and usable on all providers ?

Sounds great, please go ahead

@PierreBeucher
Copy link
Copy Markdown
Owner

Continued work on #372 - I made sure to keep your commit history as-is for your contributions :)

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.

2 participants