I have 2 EC2 instances (application and bastion) and one RDS instance (database) in AWS. I want my RDS to only be accessible through the bastion (for when I as an admin want to manually change things in the database), and the application (where users can make changes to the database but only in accordance with the functionality provided by the application). For the EC2 instances, I want the bastion to be only accessible by me on my laptop (so only I can perform admin changes), and I want the application EC2 instance to be available to everyone on the internet (since its a public app). I've configured a few settings in AWS as follows, but I just wanted to confirm whether the assumptions below are correct, and whether they're enough to satisfy the requirements I've outlined above?
I've placed the application EC2 instance in a public subnet - is this enough for making sure that anyone on the internet can (safely/securely) use the app?
Under the assumption that you've set a security group on your EC2 instance that allows traffic from 0.0.0.0/0
(the public internet) on your webserver ports (probably 80 and 443) and it's placed in a public subnet, anybody on the internet can access it. If that's secure depends on your threat-model :-) - I'd definitely ensure your app provides an HTTPS endpoint for encrypted communication.
You should also add a rule to the security group that allows traffic from the bastion hosts' security group for the protocols that you require, e.g. HTTP, HTTPS, SSH.
I've placed the bastion in a different public subnet, but it requires a public/private key pair (which only I have) to access - is this enough to keep it secure?
Concerning the bastion host you seem to have placed it in a public subnet as well, which makes sense given its function. I'd advise you to make sure that the security group that's attached to it only allows traffic on port 22 (ssh) to your IP or at least not the whole internet. While public/private key auth is decent, it's just good practice to reduce the surface area for attackers.
Based on my RDS instance's availability zone, it seems to be in a subnet that when I check the route table, destination 0.0.0.0/0 has target set to iw-..... does this mean it's a public subnet, and if so is there a way to smoothly move it to a private subnet without losing connectivity to the application and bastion EC2 instances? I've also set public accessibility to no, so does that mean it can't be accessed publicly even if it's in a public subnet?
The instance is in a public subnet from what you describe. A subnet is public by definition if the routing table has a direct route to the internet gateway (the igw-... entry). This would make your database accessible from the internet in principle. However: you seem to have set public accessibility to no, which results in the database not getting a public IP address. This in effect makes sure, it can't be reached from the internet so only within your VPC.
It would be good practice to move it into a private subnet, but that would incur a downtime. I'd also argue that it doesn't matter from a purely technical perspective, because RDS doesn't need/use internet connectivity and with a decent security group you'll be fine (moving it would still be something to consider for a clean setup).
My suggestions would be to set up the security group for the database to allow only traffic on the database port from the application servers security group and the bastion hosts security group.
With the changes to the security groups I outlined, this setup should fulfill your requirements.