node.jsamazon-web-servicesnginxapi-gateway

How to restrict access to AWS Express API via specific IP and have HTTPS certificate


I have an API on AWS that is restricted to only be accessed via my other AWS website domain (which sits on a different EC2 instance).

Currently this is over HTTP. When trying to create a certificate for it, either via letsencrypt or AWS, it won't work because it's not public.

Should I just allow HTTPS access to the API via all IPs: 0.0.0.0 and then restrict it further via the origin inside Nginx or Node.js?

Note: AWS gives a warning here:

Rules with source of 0.0.0.0/0 or ::/0 allow all IP addresses to access your instance. We recommend setting security group rules to allow access from known IP addresses only.

Yet the above warning is confusing because this other AWS document contradicts it by saying you need to enable HTTPS.

What is best practice?


Solution

  • Thanks in part to @MarkB in comments and this guide I've found a solution using manual DNS record verification

    1. Keep the restricted IP access from my other domain (don't have to make it open on all ports 0.0.0.0)

    2. Use DNS record verification. From AWS EC2 instance, inside the terminal:

      sudo certbot certonly --manual --preferred-challenge dns
      

      This creates a temporary DNS record, which I can add to route 53 hosted zone for the API.

      _acme-challenge.api.MYDOMAIN
      qmsLAetc....
      
    3. Add that record to my hosted zone as type TXT:

      • _acme-challenge.api.MYDOMAIN TXT Simple - No "uY-yYcETC...." 300
    4. Inside terminal - press Enter to continue

    5. chmod to the newly created SSL cert: sudo chmod 755 /etc/letsencrypt/live

    6. Update Nginx to support SSL and redirect HTTP to HTTPS

      server {
        listen  80;
        server_name *.MYDOMAIN;
        return 301 https://$host$request_uri;
      }
      
      server {
        listen  443 ssl;
        ssl_certificate /etc/letsencrypt/live/api.MYDOMAIN/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/api.MYDOMAIN/privkey.pem;
        ssl_protocols       TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
        ssl_ciphers         HIGH:!aNULL:!MD5;
        server_name *.MYDOMAIN.co.uk;
      
    7. Test Nginx: sudo nginx -t

    8. Restart Nginx: sudo systemctl restart nginx

    9. Test HTTPS location: curl https://api.MYDOMAIN/healthcheck

    It works!