Technical Forum
Ask questions. Discover Answers.
cancel
Showing results for 
Search instead for 
Did you mean: 

How to tell nginx to use a forward proxy to reach a specific destination

pepito
Altocumulus
Altocumulus

Hello.

I accidentally closed my previous post, so I recreate this discussion because of the following problem I'm encountering.

Here is the situation :

  • I have multiple servers which are in a secure network zone
  • I have another server where nginx is installed and is used as a reverse proxy.
  • The NGINX server has access to a remote destination (a gitlab server) through a forward proxy (squid)

So the flow is the following : Servers in secure zone --> Server Nginx as reverse proxy --> Server squid as forward proxy --> an internal gitlab in another network zone.

Is it possible to tell nginx to use the squid forward proxy to reach the gitlab server, please ?

For the moment, I have this configuration :

server {
  listen 443 ssl;
  server_name <ALIAS DNS OF NGINX SERVER>;

  ssl_certificate /etc/nginx/certs/mycert.crt;
  ssl_certificate_key /etc/nginx/certs/mykey.key;
  ssl_session_cache shared:SSL:1m;
  ssl_prefer_server_ciphers   on;

  access_log /var/log/nginx/mylog.access.log;
  error_log  /var/log/nginx/mylog.error.log debug;

  location / {
    proxy_pass https://the-gitlab-host:443;
  }
}

But it does not work. When I try to perform a git command from a server in secure zone, it fails and in the nginx logs I see a timeout, which is normal, because nginx does not use the squid forward proxy to reach the gitlab server.

Thank you in advance for your help !

Best regards.

1 ACCEPTED SOLUTION

Seems I have also made a mistake. In setting a proxy setting for git it treats the NGINX as a forward proxy. The issue with this is NGINX is a reverse proxy. In effect it acts as an endpoint for the forward proxy you are trying to reach.

The error is due to NGINX  trying to interpret a forward proxy request. So I figure we need to tell it to not do any processing on the traffic and we do that with the stream command which passes the TCP stream directly to the destination.

stream {
    upstream web_server {
        # Our web server, listening for SSL traffic
        # Note the web server will expect traffic
        # at this xip.io "domain", just for our
        # example here
        server PROXYIP:PROXYPORT;
    }

    server {
        listen 443;
        proxy_pass web_server;
    }
}

The issue with this is it intercepts ALL traffic on 443. If you dont want that then have it listen on a different port and adjust the .gitconfig to specify its proxy on the new port. You cannot tell it to match a name because at the TCP layer there is no server name. 


View solution in original post

12 REPLIES 12

In order to pickup the request for gitlab you need to specify the server_name to match.

server_name the-gitlab-host;

Secondly you need to specify the forward proxy IP address (x.x.x.x) and not name. Then tell NGINX to use the original host. I've included some other useful settings as well.

proxy_set_header Host $http_host;
proxy_connect_timeout 60;
proxy_read_timeout 60;
proxy_send_timeout 60;
proxy_intercept_errors off;
proxy_http_version 1.1;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass https://x.x.x.x:443

Now when the request arrives at NGINX it will match the server name and forward it to the forward proxy leaving the Host intact. Documentation available here - https://docs.nginx.com/nginx/admin-guide/web-server/reverse-proxy/

Hello Kevin.

Thank you for your answer.

For the moment, as the server name, I created an alias DNS which target the IP of the nginx server.

<ALIAS DNS OF NGINX SERVER>

If I set the server name as the DNS alias of the gitlab host instead, the servers in the secure zone won't go through nginx, no ? They will contact the DNS servers and they will try to connect directly to gitlab host, which is not possible, because they are in a secure zone without direct access to the gitlab host.

Tell me if I am wrong. How can your solution work please when there are DNS servers involved ?

Best regards.

@pepito - your replies were caught in our SPAM system over our weekend. I released them all.
If one of these is best/most complete follow up then you can (or I can) delete the others as duplicates. 
Sorry about the hassle.

Hello Lief, thank you for your answer.

I can edit some of my replies to add "duplicated reply to be removed", but for some other replies, I don't have the choice "Edit reply" unfortunately.

You can delete all of my replies except the one from 04-Nov-2022 16:38.

Best regards

Thanks @pepito - I think I've got it all sorted out. Thanks for your patience and for contributing to our community.

(Note: The reason you could edit some replies and not others is because there is a time-limit on editing Questions and Replies in our forum. I think the cutoff is 24 calendar hours. This is a feature designed to protect the integrity of a question so MemberA does not edit a question several days after it has been answered in a way that then invalidates any answers.)

Hello.

 

1. Your solution

I tried your solution, here is the nginx configuration.

<GITLAB-HOST> is the host of my gitlab gitlab.mycompany.com.

server {
  listen 443 ssl;
  server_name <GITLAB-HOST>;

  ssl_certificate /etc/nginx/certs/mycert.crt;
  ssl_certificate_key /etc/nginx/certs/mykey.key;
  ssl_session_cache shared:SSL:1m;
  ssl_prefer_server_ciphers   on;

  access_log /var/log/nginx/mysite.access.log;
  error_log  /var/log/nginx/mysite.error.log debug;

  location / {
    proxy_set_header Host $http_host;
    proxy_connect_timeout 60;
    proxy_read_timeout 60;
    proxy_send_timeout 60;
    proxy_intercept_errors off;
    proxy_http_version 1.1;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_pass https://<PROXY-IP>:<PROXY-PORT>;
  }
}

I got a timeout error message when I tried git clone command from a client.

git clone https://<LOGIN>@<GITLAB-HOST>/myrepo.git
Cloning into 'myrepo'...
fatal: unable to access 'https://<LOGIN>@<GITLAB-HOST>/myrepo.git/': Failed connect to <GITLAB-HOST>:443; Connection timed out

I think it is normal to get a timeout, because the client tries to reach directly <GITLAB-HOST> without passing through nginx, and direct connections are impossible. I saw nothing in the nginx logs.

 

2. Second retry

I tried the following modification from your solution.

<DNS-ALIAS-NGINX-GITLAB> is an alias DNS that I created especially for clients to send git requests to nginx host.

server {
  listen 443 ssl;
  server_name <DNS-ALIAS-NGINX-GITLAB>;

  ssl_certificate /etc/nginx/certs/mycert.crt;
  ssl_certificate_key /etc/nginx/certs/mykey.key;
  ssl_session_cache shared:SSL:1m;
  ssl_prefer_server_ciphers   on;

  access_log /var/log/nginx/mysite.access.log;
  error_log  /var/log/nginx/mysite.error.log debug;

  location / {
    proxy_set_header Host $http_host;
    proxy_connect_timeout 60;
    proxy_read_timeout 60;
    proxy_send_timeout 60;
    proxy_intercept_errors off;
    proxy_http_version 1.1;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_pass https://<PROXY-IP>:<PROXY-PORT>;
  }
}

When I retried my git clone command, I got this error :

git clone https://<LOGIN>@<DNS-ALIAS-NGINX-GITLAB>/myrepo.git
Cloning into 'myrepo'...
fatal: unable to access 'https://<LOGIN>@<DNS-ALIAS-NGINX-GITLAB>/myrepo.git/': The requested URL returned error: 502

I saw this error message in nginx logs :

2022/11/04 16:16:23 [error] 18473#18473: *1 SSL_do_handshake() failed (SSL: error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol) while SSL handshaking to upstream, client: <CLIENT-IP>, server: <DNS-ALIAS-NGINX-GITLAB>, request: "GET /myrepo.git/info/refs?service=git-upload-pack HTTP/1.1", upstream: "https://<PROXY-IP>:<PROXY-PORT>/myrepo.git/info/refs?service=git-upload-pack", host: "<DNS-ALIAS-NGINX-GITLAB>"

I don't really understand the error here, but something bothers me in this solution. I never mention in nginx conf the final host, so I don't think it can work.

 

3. Another modification

I tried to set the Host header to the final gitlab-host. As the forward proxy and the gitlab host listens to different port number, I had to specify the final gitlab-host port in the Host header.

server {
  listen 443 ssl;
  server_name <DNS-ALIAS-NGINX-GITLAB>;

  ssl_certificate /etc/nginx/certs/mycrt.crt;
  ssl_certificate_key /etc/nginx/certs/mykey.key;
  ssl_session_cache shared:SSL:1m;
  ssl_prefer_server_ciphers   on;

  access_log /var/log/nginx/mysite.access.log;
  error_log  /var/log/nginx/mysite.error.log debug;

  location / {
    proxy_set_header Host <GITLAB-HOST>:443;
    proxy_connect_timeout 60;
    proxy_read_timeout 60;
    proxy_send_timeout 60;
    proxy_intercept_errors off;
    proxy_http_version 1.1;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_pass https://<PROXY-IP>:<PROXY-PORT>;
  }
}

But I got the same error than in the second try (in my git command and in nginx logs)

git clone https://<LOGIN>@<DNS-ALIAS-NGINX-GITLAB>/myrepo.git
Cloning into 'myrepo'...
fatal: unable to access 'https://<LOGIN>@<DNS-ALIAS-NGINX-GITLAB>/myrepo.git/': The requested URL returned error: 502
2022/11/04 16:34:39 [error] 20677#20677: *5 SSL_do_handshake() failed (SSL: error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol) while SSL handshaking to upstream, client: <CLIENT-IP>, server: <DNS-ALIAS-NGINX-GITLAB>, request: "GET /myrepo.git/info/refs?service=git-upload-pack HTTP/1.1", upstream: "https://<PROXY-IP>:<PROXY-PORT>/myrepo.git/info/refs?service=git-upload-pack", host: "<DNS-ALIAS-NGINX-GITLAB>"

 

May you help me to succeed, please ?

Best regards.

 

Oh so sorry to hear your posts were blocked. Can you flag the duplicates you want removed and Lief will looks after it for you. Let me have a read through the ongoing troubleshooting for you. I will post shortly.

You have to tell git to use a proxy. Add this to your .gitconfig file using the nginx proxy IP address (not name) and port it is listening on. Post the the section below after you have configured it. Return the nginx config to the one I posted as you showed in 1 Your Solution.

[http]
[http "https://github.com"]
	proxy = http://nginx_proxy_address:nginx_proxy_port  

In regards to the settings I provided, here is more intel

server_name gitlab-host;
proxy_set_header Host $http_host;
proxy_pass https://<proxy-ip:proxy-port;

Request from client arrives at proxy due to .gitconfig settings. On arrival NGINX tries to match it with a server confguration. It sees this configuration is listening on 443, then is sees this config server name matches the destination host in the request. This tells nginx to use this configuration for the incoming connection.

The proxy pass directive indicates there is an upstream proxy that needs to be used. So NGINX modifies the request for a proxy destination, much like the client did when it was accessing NGINX. However being a forward proxy we do not want that as it will be transparently passing traffic to the internet. So we restore the normal host from the incoming http_request using $http_host. You can try with and without this option to see what works. The forward proxy may accept proxy format requests as well.

Hello !

Thank you for your answer.

Here is NGINX configuration :

 

server {
  listen 443 ssl;
  server_name <GITLAB-HOST>;

  ssl_certificate /etc/nginx/certs/mycert.crt;
  ssl_certificate_key /etc/nginx/certs/mykey.key;
  ssl_session_cache shared:SSL:1m;
  ssl_prefer_server_ciphers   on;

  access_log /var/log/nginx/access.log;
  error_log  /var/log/nginx/error.log debug;

  location / {
    proxy_set_header Host $http_host;
    proxy_connect_timeout 60;
    proxy_read_timeout 60;
    proxy_send_timeout 60;
    proxy_intercept_errors off;
    proxy_http_version 1.1;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_pass https://<PROXY-IP>:<PROXY-PORT>;
  }
}

 

 

And here is the .gitconfig file from a client :

 

[http]
        sslCAInfo = <path-to-truststore>
[http "https://<GITLAB-HOST>"]
        proxy = https://<NGINX-HOST-IP>:443
        sslCAInfo = <path-to-truststore>

 

 

Here is the git command that I perform on the client and the result :

 

git clone https://<LOGIN>@<GITLAB-HOST>/myrepo.git
Cloning into 'myrepo'...
fatal: unable to access 'https://<GITLAB-HOST>/myrepo.git/': Failed connect to <GITLAB-HOST>:443; Connection timed out

 

 

Here is the version of git CLI on the client :

 

git-1.8.3.1-23.el7_8.x86_64

 

 

And my version of NGINX on the nginx host :

 

# nginx -v
nginx version: nginx/1.20.2

 

 

I tried something else in my ~/.gitconfig file. I kept only one http section :

 

[http]
  sslCAInfo = <truststore-path>
  proxy = https://<NGINX-HOST-IP>:443

 

But now, I have a 400 http error when I execute the git command :

 

git clone https://<LOGIN>@<GITLAB-HOST>/myrepo.git
Cloning into 'myrepo'...
fatal: unable to access 'https://<GITLAB-HOST>/myrepo.git/': Received HTTP code 400 from proxy after CONNECT

 

And I can see this in NGINX logs :

 

2022/11/08 10:25:09 [info] 24032#24032: *1 client sent invalid request while reading client request line, client: <CLIENT-IP>, server: <GITLAB-HOST>, request: "CONNECT <GITLAB-HOST>:443 HTTP/1.1"

 

Thank you in advance !

Seems I have also made a mistake. In setting a proxy setting for git it treats the NGINX as a forward proxy. The issue with this is NGINX is a reverse proxy. In effect it acts as an endpoint for the forward proxy you are trying to reach.

The error is due to NGINX  trying to interpret a forward proxy request. So I figure we need to tell it to not do any processing on the traffic and we do that with the stream command which passes the TCP stream directly to the destination.

stream {
    upstream web_server {
        # Our web server, listening for SSL traffic
        # Note the web server will expect traffic
        # at this xip.io "domain", just for our
        # example here
        server PROXYIP:PROXYPORT;
    }

    server {
        listen 443;
        proxy_pass web_server;
    }
}

The issue with this is it intercepts ALL traffic on 443. If you dont want that then have it listen on a different port and adjust the .gitconfig to specify its proxy on the new port. You cannot tell it to match a name because at the TCP layer there is no server name. 


Hello Kevin.

I tried your solution and it works fine, thanks for your help !

Best regards.

docker
Nimbostratus
Nimbostratus

In addition to the aforementioned answers, there exists a graphical user interface (GUI) known as Nginx Proxy Manager, which may appeal to individuals who prefer not to work with code directly. The suggested approach to utilizing the Nginx Proxy Manager involves installing it on Docker and utilizing it to forward traffic to Docker containers within the same network. Following installation, generating SSL certificates is a simple process that can be achieved with a single click.

  1. Source Video:    Nginx Proxy Manager
  2. Official Website: Nginx Proxy Manager