My environment
I have AWS API Gateway with Elastic Beanstalk. I want to use Client Side Certificate validation on host side (Elastic Beanstalk). Elastic Beanstalk consists of Load Balancer (ELB) and EC2 with NGINX and my Ruby on Rails app. I've generated client side certificate on API Gateway. The current flow is:
- API Gateway sends request
- this request goes through Elastic Load Balancer (TCP port 80) and sends it futher to EC2 instance on port 80 (TCP)
- on EC2 instance I have NGINX running in Docker. NGINX container listens on port 443, which is bind to host's port 80.
API Gateway -> (TCP 80) ELB (TCP 80) -> (port 80) host -> (port 443) NGINX container
My problem
I use following nginx.conf, where I try to make client side certificate verification:
user root;
error_log /var/log/app-nginx-error.log debug;
pid /var/run/app-nginx.pid;
events {
worker_connections 8096;
multi_accept on;
use epoll;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" "$ssl_client_cert"';
access_log /var/log/app-nginx-access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 10;
upstream appserver {
server unix:///var/run/puma.sock;
}
server {
listen 443 default_server;
root /var/www/public;
client_max_body_size 16m;
ssl_trusted_certificate /etc/nginx/ssl/api-gateway.pem;
ssl_client_certificate /etc/nginx/ssl/api-gateway.pem;
ssl_verify_client on;
ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES";
ssl_prefer_server_ciphers on;
if ($ssl_client_verify = FAILED) {
return 495;
}
if ($ssl_client_verify = NONE) {
return 402;
}
if ($ssl_client_verify != SUCCESS) {
return 403;
}
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
try_files $uri/index.html $uri @appserver;
location @appserver {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header Client-IP $remote_addr;
proxy_pass http://appserver;
proxy_set_header X-Client-Verify $ssl_client_verify;
}
access_log /var/log/app-nginx-access.log;
error_log /var/log/app-nginx-error.log debug;
error_page 500 502 503 504 /500.html;
}
}
However, when I test it with API Gateway sending Cert it always returns 403 - it comes from following block:
if ($ssl_client_verify != SUCCESS) {
return 403;
}
Strange thing for me is that it doesn't go into any previous if statement with FAILED or NONE.
If I remove:
ssl_verify_client on;
It also goes into:
if ($ssl_client_verify != SUCCESS) {
return 403;
}
If I put again ssl_verify_client on and remove this if statement:
if ($ssl_client_verify != SUCCESS) {
return 403;
}
Everything is passed forward - it doesn't matter if I send request with certificate or without certificate.
My questions
- Is my nginx.conf OK? (Maybe I've mixed something with TCP / HTTP ?)
- Is there any way to get more details what comes to NGINX (is there any certificate?), what is the result of ssl_verify_client and anything what could help to detect the problem?