2
votes

I'm creating a simple website on AWS Elastic Beanstalk using node js. I'm trying to add an SSL certificate to the EC2 instance but it keeps saying

"Error: listen EACCES: permission denied 0.0.0.0:443"

What did I miss?

EC2 Security Group:

Inbound Rules:

HTTP    TCP    80    0.0.0.0/0
HTTP    TCP    80    ::/0
HTTPS   TCP    443   0.0.0.0/0
HTTPS   TCP    443   ::/0

Outbound Rules:

All traffic    All    All    0.0.0.0/0

Node JS:

    var ipaddress = "0.0.0.0";
    var port = 443;

    var options = {
        key: sslKey,
        cert: sslCert,
        ca: [sslCa]
    }

    server = https.createServer(options, handleRequest);

    server.listen(port, ipaddress, function () {
        console.log("Server listening on port "+port);
    });
2
are you using LB on Elastic beanstalk? if yes then do not manage ssl at instance level - Adiii
Not using a LB to save $cash. I'll try out that guide. - Gordon Truslove

2 Answers

2
votes

I know this Elastic Beanstalk stuff is not documented well, but since I did the AWS DevOps certification some time ago which covered this, I remember some points:

  • You should bind your HTTP server to 0.0.0.0. I see you already did that.
  • Your app is not running with root privileges on your EB instance. Usually what they want you to do - probably for security reasons - is to proxy your connection through the nginx proxy which comes pre-configured on your instance. They pass the PORT environment variable to your node.js app and you should use it to listen for upstream traffic by the proxy. [1]
  • For SSL termination on your nginx proxy to work, you must then configure ssl on the proxy accordingly as already pointed out correctly by vikyol. [2]
  • The connection between the proxy and your app will then be unencrypted. This should not be an issue since it does not leave the machine in between.

Some more thoughts

  • I would prefer SSL termination on the load balancer for performance reasons if you have some $$ somewhen.
  • SSL Certificate management usually is much more comfortable via ACM and ELB.

References

[1] https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/nodejs-platform-proxy.html
[2] https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/https-singleinstance-nodejs.html

1
votes

On some Linux distros, notably Ubuntu, non-root users cannot bind to ports less than 1024. (See my notes below for evidence.)

There are various ways to address this. See:


More generally though, to answer the question of "how to I get a SSL-secured website running on Elastic Beanstalk". I would say: Don't terminate your TLS/SSL in your application.

  • In my experience, using an Elastic Load Balancer to terminate your SSL/TLS is an altogether far simpler solution. (If you use Amazon Certificate Manager (ACM) to issue and manage your certs). ACM will automatically update certificate before expiration too!

  • If you do really want to keep your cert on-instance, then I would recommend you use a "real" web server, like nginx to front your nodejs process. nginx has facilities to install your SSL certs[1] and plugins to automate their issuance and renewal via LetsEncrypt[2]. And quite simply that's what its designed to do.

    • I don't know which specific EB platform you're using, but many of them (all of them?) come pre-installed with web server's running by default. (e.g., the docker platform runs nginx, the python platform runs apache). As such, it is likely you already have an appropriate web server installed on your EB instance.

The following was executed on an Ubuntu docker instance. (The command is just a one-line command to start a python HTTP server at the specified port.) You can see that port 1025 always works. But port 1023 only works as root.

root@24928b62f0bd:/# python3 -m http.server 1025
Serving HTTP on 0.0.0.0 port 1025 (http://0.0.0.0:1025/) ...
^C
Keyboard interrupt received, exiting.
root@24928b62f0bd:/# python3 -m http.server 1023 
Serving HTTP on 0.0.0.0 port 444 (http://0.0.0.0:1023/) ...
^C
Keyboard interrupt received, exiting.
root@24928b62f0bd:/# useradd bob
root@24928b62f0bd:/# su bob
$ python3 -m http.server 1025
Serving HTTP on 0.0.0.0 port 1025 (http://0.0.0.0:1025/) ...
^C
Keyboard interrupt received, exiting.
$ python3 -m http.server 1023
Traceback (most recent call last):
  File "/usr/lib/python3.6/runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/usr/lib/python3.6/http/server.py", line 1211, in <module>
    test(HandlerClass=handler_class, port=args.port, bind=args.bind)
  File "/usr/lib/python3.6/http/server.py", line 1185, in test
    with ServerClass(server_address, HandlerClass) as httpd:
  File "/usr/lib/python3.6/socketserver.py", line 456, in __init__
    self.server_bind()
  File "/usr/lib/python3.6/http/server.py", line 136, in server_bind
    socketserver.TCPServer.server_bind(self)
  File "/usr/lib/python3.6/socketserver.py", line 470, in server_bind
    self.socket.bind(self.server_address)
PermissionError: [Errno 13] Permission denied
$ 

[1] http://nginx.org/en/docs/http/configuring_https_servers.html [2] https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-18-04