Reverse proxy localhost from docker

1. Caddy version (caddy version):

2.2.1

2. How I run Caddy:

a. System environment:

Ununtu 20.04.2, Docker

b. Command:

See docker-compose.yml

c. Service/unit/compose file:

version: "3.7"

services:

  caddy:
      image: caddy:2.2.1
      container_name: caddy
      hostname: caddy
      restart: unless-stopped
      ports:
        - "80:80"
        - "443:443"
      environment:
        - PUID=$PUID
        - PGID=$PGID
      volumes:
        - ./Caddyfile:/etc/caddy/Caddyfile:ro
        - $CONFIGS/caddy/data:/data
        - $CONFIGS/caddy:/config

networks:
  default:
    external:
      name: caddy_net

d. My complete Caddyfile or JSON config:

domain.com {
    route /netdata* {      
      reverse_proxy localhost:19999
    }
}

3. The problem I’m having:

I cannot get netdata to proxy through caddy. It is running fine on its default port 19999. I have used this strategy to proxy other things locally hosted or other containers. For some reason not working with Netdata. I am new to Caddy v2.

4. Error messages and/or full log output:

{"level":"error","ts":1612393641.7975783,"logger":"http.log.error","msg":"dial tcp 127.0.0.1:19999: connect: connection refused","request":{"remote_addr":"10.0.0.11:60879","proto":"HTTP/2.0","method":"GET","host":"domain.com","uri":"/netdata","headers":{"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"],"Sec-Fetch-Site":["none"],"Sec-Fetch-User":["?1"],"Sec-Fetch-Dest":["document"],"Accept-Language":["en-US,en;q=0.9"],"Cookie":["organizrLanguage=en; JSESSIONID=A3D7579FD29302039983007AEF3790FA],"Upgrade-Insecure-Requests":["1"],"User-Agent":["Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36"],"Sec-Fetch-Mode":["navigate"],"Accept-Encoding":["gzip, deflate, br"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","proto_mutual":true,"server_name":"domain.com"}},"duration":0.001979163,"status":502,"err_id":"ewqz1c52k","err_trace":"reverseproxy.(*Handler).ServeHTTP (reverseproxy.go:441)"}

5. What I already tried:

I have used this same strategy to proxy things that are locally hosted on the system without issue. Not sure what’s up here.

6. Links to relevant resources:

When in docker, localhost means “this same container”, i.e. Caddy’s own container.

If you want to proxy to another container, you need to use that container’s IP address, or service name. So you probably want reverse_proxy netdata:19999

Also, please upgrade to Caddy v2.3.0! :grinning_face_with_smiling_eyes:

Since I see you’re proxying on a subpath, here’s some additional reading materials about that:

I would recommend proxying with a subdomain like netdata.example.com instead of a subpath.

1 Like

Thanks @francislavoie!

Netdata in this case is not another container it’s just locally installed. So I can reference the machine’s own IP on a subdomain. Not sure if there’s a more elegant way to do this.

netdata.domain.com {
  reverse_proxy 10.0.2.1:19999
}

I was trying to keep it on a subpath of root since I have some existing OAuth set up there with caddy-auth-portal. Would be nice to have that cover netdata too without duplicating the config.

Perhaps running netdata as a container instead would allow it to work on the subpath.

Unfortunately, that part of Docker is still a total mess.

/cc @greenpau what’s the recommended way to make auth work on subdomains as well?

@francislavoie , @fburn , need to see the config to complete my understanding of this setup.

2 Likes

@greenpau thanks for creating these plugins.

After building caddy container with auth-portal and auth-jwt, here is my Caddyfile.

Possible to get auth on the subdomain too?

domain.com {
  
  route /auth* {
        auth_portal {
            path /auth
            backends {
                google_oauth2_backend {
                    method oauth2
                    realm google
                    provider google
                    client_id xxxx
                    client_secret xxxx
                    scopes openid email profile
                    user xxxx@xxxx add role verified
                }
            }
            jwt {
                token_name access_token
                token_secret xxxx
                token_lifetime 604800
            }
        }
    }

    route /* {
        jwt {
          primary yes
          trusted_tokens {
            static_secret {
              token_name access_token
              token_secret xxxx
            }
          }
          auth_url /auth
          allow roles verified
        }
    reverse_proxy organizr:80
    }

    route /sonarr* {      
      jwt
      reverse_proxy sonarr:8989
    }
}

netdata.domain.com { 
    
    # can I have auth here too without duplicating above? 

    reverse_proxy 10.0.2.1:19999
}

@fburn , please look at portal docs for cookie_domain. I think you could issue a cookie that will be available in subdomains.

What happens when you add jwt directive prior to reverse proxy in the subdomain?

1 Like

@greenpau, in this case caddy will complain:

netdata.domain.com { 
    jwt
    reverse_proxy 10.0.2.1:19999
}

Results in:
run: adapting config using caddyfile: directive 'jwt' is not ordered, so it cannot be used here

You should probably just remove /*. It’s very slightly faster. Right now you’re asking Caddy to do a prefix match for / which will always be true, whereas without it, Caddy won’t need to do any extra checks.

Also FYI, your routes will be sorted by the Caddyfile adapter with least specific path matchers going last.

Yep, you need to use route around it.

	route {
		jwt
		reverse_proxy 10.0.2.1:19999
	}

This is because directives from plugins aren’t in the directive ordering table, so you need to force them to have one by using route. See:

@fburn . please see the discussion in http - Share cookie between subdomain and domain - Stack Overflow

I would say it is on of the below:

cookie_domain domain.com
cookie_domain .domain.com

2 Likes

Thank you. One day I will understand Caddy :slight_smile:

Amazing, this pointed me in the right direction.

For posterity, here is the full Caddyfile:

domain.com {
  route /auth* {
        auth_portal {
            path /auth
            backends {
                google_oauth2_backend {
                    method oauth2
                    realm google
                    provider google
                    client_id xxxx
                    client_secret xxxx
                    scopes openid email profile
                    user xxxx@xxxx add role verified
                }
            }
            jwt {
                token_name access_token
                token_secret xxxx
                token_lifetime 604800
            }
            cookie_domain domain.com # define domain here
        }
    }
    route / {
        jwt {
          primary yes
          trusted_tokens {
            static_secret {
              token_name access_token
              token_secret xxxx
            }
          }
          auth_url /auth
          allow roles verified
        }
    reverse_proxy organizr:80
    }
    route /sonarr* {      
        jwt
        reverse_proxy sonarr:8989
    }
}

netdata.carldog.com {
  route {
    jwt {
          auth_url https://domain.com/auth
          allow roles verified
        }
    reverse_proxy 10.0.2.1:19999
    
  }
}
1 Like

@fburn , are you all set?

1 Like

@greenpau, yes this is solved, thanks!

1 Like

This topic was automatically closed after 30 days. New replies are no longer allowed.