Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(conf) add port_maps configuration option #5861

Merged
merged 2 commits into from
May 29, 2020
Merged

Conversation

bungle
Copy link
Member

@bungle bungle commented May 6, 2020

Summary

Layer 4 port mapping (or port forwarding) is used a lot when running Kong inside a container. Common example is exposing ports 80 and 443 externally while running Kong inside a container with internal ports 8000 and 8443. It also allows Kong to started without elevated privileges as it is quite common that you need root-access to bind processes to TCP port < 1024.

As this translation is done in many cases without using layer 4 proxy_protocol or layer 7 X-Forwarded-Port HTTP header, the Kong has no knowledge about the target port that client originally connected to.

This PR adds port_maps configuration parameter:

#port_maps =                     # With this configuration parameter you can
                                 # let the Kong to know about the port from
                                 # which the packets are forwarded to it. This
                                 # is fairly common when running Kong in a
                                 # containerized or virtualized environment.
                                 # For example `port_maps=80:8000, 443:8443`
                                 # instructs Kong that the port 80 is mapped
                                 # to 8000 (and the port 443 to 8443), where
                                 # 8000 and 8443 are the ports the Kong is
                                 # listening to.
                                 #
                                 # This parameter helps Kong to set proper
                                 # forwarded upstream HTTP request header or to
                                 # get proper forwarded port with a Kong PDK
                                 # (in case other means determining it have
                                 # failed). It changes routing by a destination
                                 # port to route by a port from which packets
                                 # are forwarded to Kong, and similarly it
                                 # changes the default plugin log serializer to
                                 # use the port according to this mapping
                                 # instead of reporting the port Kong is
                                 # listening to.

This gets parsed into kong.configuration.host_ports table.

E.g. KONG_PORT_MAP="80:8000,443:8443" where the first number (80/443) before : is the host published port and the second one (8000/8443) is the port that Kong is listening translates to following kong.configuration.host_ports:

{
  [8000]   = 80,
  ["8000"] = 80,
  [8443]   = 443,
  ["8443"] = 443,
}

This PR also adds a new nginx context variable ngx.ctx.host_port which contains this translated port. So instead of using ngx.var.server_port the more correct one in many cases is ngx.ctx.host_port.

Kong PDK is also changed to take this in account when using:

local port = kong.request.get_forwarded_port()

This commit also changes the X-Forwarded-Port to use this. Additionally stream routing by destination port will use this too. Basic log serializer was changed to use ngx.var.host_port as well.

Currently it is only usable in proxy/stream, so things like admin api is not affected by this.

@bungle bungle added the pr/wip A work in progress PR opened to receive feedback label May 6, 2020
@Tieske
Copy link
Member

Tieske commented May 6, 2020

I think I mentioned this elsewhere, but can we add a warning in the logs when Kong is started in a container, and this option is not set?

@bungle bungle force-pushed the feat/conf-portmap branch 3 times, most recently from 0879676 to 81c3555 Compare May 7, 2020 18:25
@bungle bungle force-pushed the feat/conf-portmap branch 3 times, most recently from 127ee2d to 9eb3c9a Compare May 8, 2020 14:03
@hbagdi
Copy link
Member

hbagdi commented May 8, 2020

Additionally stream routing by destination port will use this too. Basic log serializer was changed to use ngx.var.host_port as well.

I would recommend that we start small rather than going big from the beginning. The primary motivation for this feature is to correct set x-forwarded-port problem when upstream services depend on it. I have not heard any problems with other cases that this PR targets. Do we have feature requests or complaints to change the log serializer and stream routing itself?

I've consulted with users on stream proxy and I do know of users who map different port on different LBs to the same port on Kong and then they route based on the port on Kong. This would be a break change for such setups. These should be one-offs and rare but my point stands.

@bungle
Copy link
Member Author

bungle commented May 9, 2020

Additionally stream routing by destination port will use this too. Basic log serializer was changed to use ngx.var.host_port as well.

I would recommend that we start small rather than going big from the beginning. The primary motivation for this feature is to correct set x-forwarded-port problem when upstream services depend on it. I have not heard any problems with other cases that this PR targets. Do we have feature requests or complaints to change the log serializer and stream routing itself?

I've consulted with users on stream proxy and I do know of users who map different port on different LBs to the same port on Kong and then they route based on the port on Kong. This would be a break change for such setups. These should be one-offs and rare but my point stands.

I don't see this being starting big. It is rather small. Nothing will be broken for anyone as nobody configures this as it is a new feature. You can also configure it for some ports and not all. In general you could also have environment where pods bind to random port. In such case you cannot practically do any routing by destination port as it is not kown before pod is launched (but the published port certainly will). This is far more common scenario.

Also if you are running LB we still respect forwarded headers before reverting to this.

@hbagdi
Copy link
Member

hbagdi commented May 11, 2020

Nothing will be broken for anyone as nobody configures this as it is a new feature. You can also configure it for sone ports and not all.

Two fair points.

In general you could also have environment where pods bind to random port. In such case you cannot practically do any routing by destination port as it is not kown before pod is launched (but the published port certainly will). This is far more common scenario.

I'm not sure if this is a common scenario. It hasn't been so in my experience.

With the points made, I think it is okay to proceed with this feature.

@bungle bungle force-pushed the feat/conf-portmap branch 2 times, most recently from 565ad73 to a6a3961 Compare May 12, 2020 19:51
@bungle bungle added pr/please review and removed pr/wip A work in progress PR opened to receive feedback labels May 12, 2020
@bungle
Copy link
Member Author

bungle commented May 13, 2020

I think I mentioned this elsewhere, but can we add a warning in the logs when Kong is started in a container, and this option is not set?

This is a bit problematic.

  1. Kong needs to know if it is a container
  2. Kong needs to know if published port is different than listen port

Perhaps later we can add support for container APIs where we can get this auto-configured.

@@ -29,6 +29,17 @@
# Each Kong process must have a separate
# working directory.

#port_map = # When running Kong behind layer 4 port mapping
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/port mapping/proxy

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean layer 4 proxy? I don't think port-forwarding/-mapping can be described as proxy. Or what do you mean by this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I rewrote the whole thing. Not sure if it is any better.

@@ -29,6 +29,17 @@
# Each Kong process must have a separate
# working directory.

#port_map = # When running Kong behind layer 4 port mapping
# (or port-forwarding), e.g. with
# `docker run -p 80:8000`, you can use this
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rather than providing commands here, it would help to provide examples like docker, L4 LBs in the cloud

# (or port-forwarding), e.g. with
# `docker run -p 80:8000`, you can use this
# parameter to let Kong know about published
# port when it differs from the one Kong is
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

published port is ambiguous because every port at kong or lb is published in some sense.

Copy link
Member Author

@bungle bungle May 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Published port is a term that docker uses for -p. The port that Kong listens (in that case) is not published e.g. you can only access it from container. For public it is published by docker at port x. What do you suggest instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I rewrote the whole thing. Not sure if it is any better.

kong.conf.default Outdated Show resolved Hide resolved
kong.conf.default Outdated Show resolved Hide resolved
kong/conf_loader.lua Outdated Show resolved Hide resolved
@@ -29,6 +29,17 @@
# Each Kong process must have a separate
# working directory.

#port_map = # When running Kong behind layer 4 port mapping
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another point, it would be better to call this setting port_maps because it can contain an array of values (csv based).

Copy link
Member Author

@bungle bungle May 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I pluralized it. We btw. have proxy_listen not proxy_listeners. And word mapping to which map refers to is ok to use in singular even in plural contexts.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. My bad.


if (host_port_num and host_port_num >= MIN_PORT and host_port_num <= MAX_PORT)
and (kong_port_num and kong_port_num >= MIN_PORT and kong_port_num <= MAX_PORT)
then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a check to ensure that kong is configured to listen on kong_port_num?
I'm not sure how much scope creep happens because of this. I'll leave that up to you, Aapo.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hbagdi I looked at it, and it is a bit complicated (listeners are parsed later). The good side is that if the port is not the one that Kong is not listening, it will not do anything. It is just excess configuration. I will create a card about this.

@bungle bungle force-pushed the feat/conf-portmap branch 2 times, most recently from 9d67a28 to dd2aad2 Compare May 19, 2020 16:08
@hbagdi hbagdi added this to the 2.1.0 milestone May 21, 2020
@hbagdi
Copy link
Member

hbagdi commented May 21, 2020

@hishamhm I added this to 2.1.0 milestone. Please change if needed.

Copy link
Member

@kikito kikito left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@bungle bungle changed the title feat(conf) add port_map configuration option feat(conf) add port_maps configuration option May 27, 2020
### Summary

Layer 4 port mapping (or port forwarding) is used a lot when running
Kong inside a container. Common example is exposing ports 80 and 443
externally while running Kong inside a container with internal ports
8000 and 8443. It also allows Kong to started without elevated
privileges as it is quite common that you need root-access to bind
processes to TCP port < 1024.

As this translation is done in many cases without using layer 4
proxy_protocol or layer 7 X-Forwarded-Port HTTP header, the Kong has
no knowledge about the target port that client originally connected
to.

This PR adds `port_maps` configuration parameter:

```
#port_maps =                     # With this configuration parameter you can
                                 # let the Kong to know about the port from
                                 # which the packets are forwarded to it. This
                                 # is fairly common when running Kong in a
                                 # containerized or virtualized environment.
                                 # For example `port_maps=80:8000, 443:8443`
                                 # instructs Kong that the port 80 is mapped
                                 # to 8000 (and the port 443 to 8443), where
                                 # 8000 and 8443 are the ports the Kong is
                                 # listening to.
                                 #
                                 # This parameter helps Kong to set proper
                                 # forwarded upstream HTTP request header or to
                                 # get proper forwarded port with a Kong PDK
                                 # (in case other means determining it have
                                 # failed). It changes routing by a destination
                                 # port to route by a port from which packets
                                 # are forwarded to Kong, and similarly it
                                 # changes the default plugin log serializer to
                                 # use the port according to this mapping
                                 # instead of reporting the port Kong is
                                 # listening to.
```

This gets parsed into `kong.configuration.host_ports` table.

E.g. `KONG_PORT_MAP="80:8000,443:8443"` where the first number (`80`/`443`) before `:`
is the host published port and the second one (`8000`/`8443`) is the port that Kong is
listening translates to following `kong.configuration.host_ports`:

```lua
{
  [8000]   = 80,
  ["8000"] = 80,
  [8443]   = 443,
  ["8443"] = 443,
}
```

This PR also adds a new nginx context variable `ngx.ctx.host_port` which
contains this translated port. So instead of using `ngx.var.server_port`
the more correct one in many cases is `ngx.ctx.host_port`.

Kong PDK is also changed to take this in account when using:
```
local port = kong.request.get_forwarded_port()
```

This commit also changes the `X-Forwarded-Port` to use this. Additionally
`stream routing` by `destination port` will use this too. Basic log
serializer was changed to use `ngx.var.host_port` as well.

Currently it is only usable in proxy/stream, so things like `admin api`
is not affected by this.
@@ -125,6 +125,29 @@
# This value can be set to `off`, thus disabling
# the plugin server and Go plugin loading.

#port_maps = # With this configuration parameter you can
# let the Kong to know about the port from
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/the Kong to/Kong/

# is fairly common when running Kong in a
# containerized or virtualized environment.
# For example `port_maps=80:8000, 443:8443`
# instructs Kong that the port 80 is mapped
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/the port/port/

# containerized or virtualized environment.
# For example `port_maps=80:8000, 443:8443`
# instructs Kong that the port 80 is mapped
# to 8000 (and the port 443 to 8443), where
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/the port/port/

# For example `port_maps=80:8000, 443:8443`
# instructs Kong that the port 80 is mapped
# to 8000 (and the port 443 to 8443), where
# 8000 and 8443 are the ports the Kong is
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"the ports on which Kong is listening"

# 8000 and 8443 are the ports the Kong is
# listening to.
#
# This parameter helps Kong to set proper
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/to set/set the/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants