Old question, but I'm studing a similar architecture on AWS, and has been a long journey.
The first question to answer is if is possible to configure AWS load balancers (ELB at the time, ALB and NLB now) to perform mutual TLS authentication.
This requires understanding of the mutual TLS authentication works. A google search can help you more than a thousand words.
To recap, in mutual TLS after the normal TLS flow, the client presents its certificate to the server. The server, upon receiving a valid and trusted certificate, extract identity information from the certificate.
In this model, the server terminating the TLS connection is the one "authenticating" the user.
Second question is how to configure AWS LB to do that.
TL;DR: I would say it can be done, but YMMV. I didn't build such a system, just studied documentation.
To achieve this we need some prerequisites:
- being able to perform TLS termination at the LB
- support PROXY PROTOCOL (both LB and backend application)
- use the certificate Common Name (or SNI) as identifier for authentication
TLS termination is supported by all AWS load balancers (since Jan 2019 NLB support this too).
This allow the client to connect successfully to the LB and have a successful TLS handshake (with a valid certificate of course).
At this point the identity of the client is known to the AWS LB.
We need to propagate this information to the backend server where our application is.
Here comes the PROXY PROTOCOL. From my understanding, thanks to field PP2_TYPE_AUTHORITY we can get the SNI and thanks to PP2_SUBTYPE_SSL_CN we can get the certificate Common Name. Those are referred as Type-Length-Value (TLV vectors) in the Proxy Protocol doc and available since revision 2017/03/10.
From the AWS docs, PROXY PROTOCOL can be configured for Classic LB and NLB Target Group.
This allow our backends to receive information about the identity of the client. The backend should then "trust" the information in the PROXY PROTOCOL to authenticate it.
We also need to make the LB aware of our certificate and trust chain. Thanks to AWS Certificate Manager we can import our certificate (body, private key and chain). After import we are able to use it as certificate in Classic LB listeners and NLB Target Groups. You either can get it from a vendor or generate it yourself, as long as your device trust the server certificate (ie a custom CA added to the client trusted store).
At this point all pieces should be in place:
- we are terminating TLS at the LB
- we are forwarding client identity to our backends (needed to authenticate client in our application)
- we are using our certificate and chain (needed to use our client certificate)
Hope it helps and please point any possible error in my reasoning. I'm still not 100% sure this would work, in particular because documentation around what is included in Proxy Protocol header from AWS LB is lacking. Is documented for Classic LB and NLB but no information is provided on TLV related to SSL (even if those are supported in Proxy Protocol v2).
A security notice: when terminating TLS at your backend servers, traffic is encrypted from device to backend. If you perform TLS termination on LB (whereas yours or AWS ones) you are obviously decrypting in before it reaches your backend server. You may use LB Backend Authentication on Classic LB, but it seems is not available for ALB or NLB. I have no knowledge of a similar feature for ALB or NLB.
I was able to find this 2016 answer by an "Amazonian" in the AWS developer forum stating that a MITM in a VPC is impossible because:
ARP poisoning is impossible due to the lack of broadcast in VPC, and IP spoofing is similarly impossible within VPC (this is because instances are not permitted to send traffic with a source IP or MAC which is not its own).
Further information is surely available if you want to go down the rabbit hole (ie this blog post of AWS Security about PCI DSS compliance and AWS VPC).