Client-side SSL not working with AWS API Gateway - ssl

I generated a client-side SSL Certificate on API Gateway and added it to my nginx configuration as below:
listen *:443;
ssl on;
server_name api.xxxx.com;
ssl_certificate /etc/letsencrypt/live/api.xxxx.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.xxxx.com/privkey.pem;
ssl_verify_client on;
ssl_client_certificate /etc/nginx/ssl/awsapigateway.crt;
location /home/ubuntu/api {
# if ($ssl_client_verify != SUCCESS) { return 403; }
# proxy_pass http://my.http.public.endpoint.com;
# proxy_set_header X-Client-Verify $ssl_client_verify;
}
The client certificate doesn't work after testing via the AWS API gateway test console. It ends up with Error 400 - No required SSL certificate was sent. API Gateway should be sending its client cert to my server with each request, so that I can validate that requests are genuinely coming from API Gateway.
I believe the reason it is not working is I am adding the PEM-encoded public key from the AWS API gateway console directly to awsapigateway.crt. Is that correct?
Additionally, does nginx support self-signed client SSL certificates, which is what AWS is providing us?

Api Gateway team here.
It looks like the nginx configuration is correct. And for our simple test case we use a node server and simply write the PEM certificate from the console directly to the crt file that is set as the ca, or in this case the ssl_client_certificate.
I'd also test using the actual deployed API if for some reason the test function in the console has an issue. Make sure to use the Stage settings to specify the cert.

Related

SSL Client Authentication with Google Cloud Run

I'm trying to move an existing backend over to Google Cloud Run. Some of the endpoints (under a specific subdomain) require SSL Client Authentication. The way this is handled at the moment is on Nginx configuration level:
server {
listen 443 ssl http2;
server_name secure.subdomain.example.com;
[...]
# SSL Client Certificate:
ssl_client_certificate xxx.pem;
ssl_verify_client on;
[...]
location / {
if ($ssl_client_verify != "SUCCESS") { return 403 $ssl_client_verify; }
[...]
}
}
What would be the best approach to handle SSL client certificate authentication with Google Cloud Run? I assume this would need some sort of load balancer on the correct network layer and with support for cloud run?
Of course there is always the option to authenticate in the ExpressJS app, but if possible I would prefer it to happen before even reaching Cloud Run.
What would be the best approach to handle SSL client certificate
authentication with Google Cloud Run?
Cloud Run does not support SSL Client Certificate Authentication. The GFE (Google Front End) proxies requests for Cloud Run applications and does not pass-through requests. The only Google Cloud load balancers that support SSL client certificates are based on Google Maglev.
None of the Google Cloud managed compute services support SSL client certificate authentication (mutual TLS authentication).
Consider using Compute Engine instead of Cloud Run.Then configure Nginx to handle client authentication. For load balancing, use a pass-through load balancer such as External TCP/UDP Network Load Balancer
You can't achieve that with Cloud Run. The SSL connection is terminated at the load balancer side (On an HTTPS load balancer, or on the Cloud Run built-in load balancer). You only receive HTTP traffic to your service.
Indeed, you can add additional security information, in the request header, but you lost the SSL flavor.

How to fix cert-manager responses to Let's Encrypt ACME challenges when using client certificate authentication on Kubernetes with nginx ingress?

We've set-up a new ingress route that requires TLS certificate authentication, and we have placed it on its own subdomain, but we are finding that cert-manager is unable to issue a certificate for it.
Using the examples provided here, we generated the CA cert and CA key, and then configured the client certificate:
https://github.com/kubernetes/ingress-nginx/tree/master/docs/examples/auth/client-certs
In the logs, I can see that all of the .acme-challenge requests are returning a 403. I am guessing that nginx is rejecting the requests because Let's Encrypt can't present a client certificate for the challenge request. What do I need to do in order to bypass the client cert requirement for ACME?
The issue here was that we actually had misconfigured the nginx.ingress.kubernetes.io/auth-tls-secret annotation. It must be in namespace/name format -- where namespace is the namespace that contains the secret containing the client CA certificate, and name is the name of that secret -- but we were only providing the name since the secret is in the same namespace with the ingress.
I was able to diagnose the issue by dumping the nginx ingress controller config to nginx.conf.txt with:
kubectl exec <NAME OF INGRESS CONTROLLER POD> -n <INGRESS NAMESPACE> -- nginx -T | tee nginx.conf.txt
(Adapted from https://docs.nginx.com/nginx-ingress-controller/troubleshooting/#checking-the-generated-config).
This included the following snippet:
## start server the.hostname.com
server {
server_name the.hostname.com ;
listen 80;
set $proxy_upstream_name "-";
set $pass_access_scheme $scheme;
set $pass_server_port $server_port;
set $best_http_host $http_host;
set $pass_port $pass_server_port;
listen 443 ssl http2;
# PEM sha: 66c07c44ba9637a23cd3d7b6ebce958e08a52ccb
ssl_certificate /etc/ingress-controller/ssl/default-fake-certificate.pem;
ssl_certificate_key /etc/ingress-controller/ssl/default-fake-certificate.pem;
ssl_certificate_by_lua_block {
certificate.call()
}
# Location denied, reason: invalid format (namespace/name) found in 'the-secret-name'
return 403;
}
## end server the.hostname.com
The key is these two lines:
# Location denied, reason: invalid format (namespace/name) found in 'the-secret-name'
return 403;
This pointed me to the annotation for the secret name. Once I fixed that, ACME worked properly.

Cloudflare SSL cert throws a Security Issue

I have configured my nginx to use the certificate and private_key that I downloaded from cloudflare crypto.
This is my nginx.conf file-
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name autocaptions.app *.autocaptions.app;
location / {
proxy_pass http://127.0.0.1:7887;
}
ssl on;
ssl_certificate /home/ubuntu/sslcerts/autocaptions.pem;
ssl_certificate_key /home/ubuntu/sslcerts/private-key.pem;
# ssl_client_certificate /home/ubuntu/sslcerts/cloudflare.crt;
# ssl_verify_client on;
}
# Redirect http to https
server {
listen 80;
listen [::]:80;
return 301 https://$host$request_uri;
}
I am not sure what the issue is. I have added the certificate and the private_key.
I see the following error in the browser when I try to access https://autocaptions.app -
Error in text-
autocaptions.app has a security policy called HTTP Strict Transport Security (HSTS), which means that Firefox can only connect to it securely. You can’t add an exception to visit this site.
In Cloudflare configuration, I have disabled HSTS, not sure why this error is showing up.
I have followed digitalocean tutorial to configure SSL.
You are using a certificate signed by the "Cloudflare Origin CA". Certificates issued by this CA are intended to be installed on your origin server so that the communication between the Cloudflare CDN and your origin server can be protected by a certificate.
These kind of certificates are not intended on systems facing end users (i.e. browsers). They are only intended to secure the communication between your origin server and Cloudflare. Typical end users will not have the "Cloudflare Origin CA" as a trusted CA in their browser and thus they will get a TLS error when connecting to your origin server - and this is thus what you get. But typical end users should not connect to the origin server in the first place - they should connect to the Cloudflare instance instead. Only Cloudflare itself should connect to the origin server and they will acknowledge their own CA as trusted.
Check your site's SSL Setting under Crypto tab. Change it to 'Full' or 'Flexible' if its on 'Full (Strict)'.
TL;DR #SteffenUllrich is absolutely correct and should be the accepted answer.
This is your current setup.
You are gray-clouding the DNS record, essentially exposing your origin server's IP address to the whole world.
The first problem here is that, it is similar to posting your home's address on Twitter/Facebook, saying the front door is unlocked so feel free to come in and take what you want!
Cloudflare Origin CA Certificate
|
client <---------------------HTTPS-----------------> your origin (AWS)
The second problem here is that CloudFlare Origin CA Certificate is not meant to be used for client-server connection. It's purpose is to encrypt connection between Cloudflare edge and your origin only. You can think of it as a self-signed certificate. This is the reason for the error you're seeing.
One very simple solution is to replace this origin certificate with other free or paid SSL certificate such as Let's Encrypt/Certbot. If you decide to go this way, you can then skip the rest of below explanation if you want.
If you wish to keep using Cloudflare Origin CA Certificate however, keep on reading.
The next step is to proxy your connection to Cloudflare by orange-clouding the DNS record. Connection between client and Cloudflare edge will be encrypted using Cloudflare's free (shared) Universal SSL Certificate. It will partially solve the problem, but only half of the client-server connection is encrypted, because you are using Flexible mode. Connection between Cloudflare edge and your origin will not be encrypted.
Universal SSL Certificate
|
client <---HTTPS---> Cloudflare edge <----HTTP----> your origin (AWS)
The final step if to change the SSL mode from Flexible to Full or Full (Strict). Now you will get end-to-end encryption.
Universal SSL Certificate Cloudflare Origin CA Certificate
| |
client <---HTTPS---> Cloudflare edge <----HTTPS----> your origin (AWS)
Any questions?

Configure Nginx to forward client certificate to backend

I have a spring boot service configured for two way ssl to verify clients using certificates. It is behind nginx proxy server. So requirements are to configure nginx to provide transparent https connection from the client and forward client certificate to the webservice(backend) to be verified. Also to configure one way ssl for other services that don't require client authentication.
Something like:
|Client| -->httpS + Client Cert--->|NGINX|--->httpS + Client Cert--->|Service 1|
|Client| ------------>httpS----------->|NGINX| ------------>http------------>|Service 2|
My nginx config:
server {
listen 443;
server_name xx.xx.xx.xxx;
ssl on;
ssl_certificate /path/to/server/cert.crt;
ssl_certificate_key /path/to/server/key.key;
ssl_client_certificate /path/to/ca.crt;
ssl_verify_client optional;
location /service1/ {
proxy_pass https://service1:80/;
#Config to forward client certificate or to forward ssl connection(handshake) to service1
}
location /service2/ {
proxy_pass http://service2:80/;
#http connection
}
}
Also, is there a way to get the common name from the certificate to verify the client and take decisions in nginx? as using the CA is not enough.
Thanks..
This is not possible. What you are attempting to do is make the nginx proxy into a "man in the middle" and this will not be allowed by TLS by design.

Nginx Proxy pass certificate autentificate to MS IIS

Nginx 1.9.5 (linux Centos7)--> MS IIS 8.5
So i try to use nginx as client revers proxy for IIS where need client certificate authentication at IIS level.
nginx:443->>IIS:443+client certificate authentications.
example location proxy pass
also here are commented commands which i try.
location ^~ /test/ {
#proxy_buffering off;
#proxy_http_version 1.0;
#proxy_request_buffering off;
#proxy_set_header Connection "Keep-Alive";
#proxy_set_header X-SSL-CERT $ssl_client_cert;
# proxy_ssl_name domain.lv;
#proxy_ssl_trusted_certificate /etc/nginx/ssl/root/CA.pem;
#proxy_ssl_verify_depth 2;
proxy_set_header HOST domain.com;
proxy_ssl_certificate /etc/nginx/ssl/test.pem;
proxy_ssl_certificate_key /etc/nginx/ssl/test_key.pem;
proxy_ssl_verify off;
proxy_pass https://10.2.4.101/;
}
At IIS simple.
create new website.
import CA cert in trusted root.
set ssl cert required.
Test what i get :
Directly browser to IIS client cert required--worked.
Nginx to other nginx client cert required--worked.
Nginx to IIS client cert ignore--worked
Nginx to IIS client cert required or accept - NOT work
ERROR:
Nginx side:
*4622 upstream timed out (110: Connection timed out) while reading response header from upstream
IIS side:
500 0 64 119971
So i hope someone could know why?
EDIT
1. also try from different server with nginx 1.8 nothing helped..
proxy_ssl_verify off;
proxy_ssl_certificate /etc/nginx/ssl/test/test.pem;
proxy_ssl_certificate_key /etc/nginx/ssl/test/test_key.pem;
proxy_pass https://domain.com;
2.Try same with apache 2.4 all worked with
SSLProxyEngine On
SSLProxyVerify none
SSLProxyCheckPeerCN off
SSLProxyCheckPeerName off
SSLProxyCheckPeerExpire off
SSLProxyMachineCertificateFile /etc/httpd/ssl/test.pem
ProxyPass "/test" "https://domain.com"
Maybe something with ssl renegotiation in nginx???
Your hunch about TLS renegotiation is correct. Nginx has not allowed TLS renegotiation since version 0.8.23 (see http://nginx.org/en/CHANGES). However, by default IIS will use TLS renegotiation when requesting a client certificate. (I haven't been able to find the reasons for this - I would be grateful if someone could enlighten me!)
You can use a packet sniffer such as wireshark to see this in action:
IIS and Nginx first perform a TLS handshake using the server certificate only.
Nginx requests the resource.
The resource requires client authentication, so IIS sends a 'Hello Request' message to Nginx to initiate TLS renegotiation.
Nginx doesn't respond to the Hello Request as TLS renegotiation has been disabled.
IIS then closes the connection as it gets no response. (See the section on renegotiation at https://technet.microsoft.com/en-us/library/cc783349(v=ws.10).aspx)
To solve this problem, you must force IIS to request a client certificate on the initial TLS handshake. You can do this using the netsh utility from powershell or the command line:
Open a powershell prompt with administrator rights.
Enter netsh
Enter http
Enter show sslcert. You should see a list of all current SSL bindings on your machine:
Make a note of the IP:port and certificate hash of the certificate that you want to enable client certificate negotiations for. We are now going to delete this binding and re-add it with the Negotiate Client Certificate property set to enabled. In this example, the IP:port is 0.0.0.0:44300 and the certificate hash is 71472159d7233d56bc90cea6d0c26f7a29db1112.
Enter delete sslcert ipport=[IP:port from above]
Enter add sslcert ipport=[IP:port from above] certhash=[certificate hash from above] appid={[any random GUID (can be the same one from the show sslcert output)]} certstorename=MY verifyclientcertrevocation=enable verifyrevocationwithcachedclientcertonly=disable clientcertnegotiation=enable
You can now confirm that this has worked by running show sslcert again. You should see an almost identical output, but with Negotiate Client Certificate set to Enabled:
Note that this method only works for individual certificates - if you need to change or renew the certificate you will have to run these steps again. Of course, you should wrap these up in a batch script or MSI installer custom action for ease of deployment and maintenance.