Kgateway
The Kgateway Emitter supports generating Gateway API and kgateway resources from Ingress manifests using:
- Provider:
ingress-nginx
Note: All other providers will be ignored by the emitter.
Usage
Ingress2gateway reads Ingress resources from a Kubernetes cluster or a file. It will output the equivalent Gateway API and kgateway-specific resources in a YAML/JSON format to stdout. The simplest case is to convert all ingresses from the ingress-nginx provider:
ingress2gateway print --providers=ingress-nginx --emitter=kgatewayThe above command will:
- Read your Kube config file to extract the cluster credentials and the current active namespace.
- Search for ingress-nginx resources in that namespace.
- Convert them to Gateway-API resources (Currently only Gateways and HTTPRoutes).
Options
print command
| Flag | Default Value | Required | Description |
|---|---|---|---|
| all-namespaces | False | No | If present, list the requested object(s) across all namespaces. Namespace in the current context is ignored even if specified with –namespace. |
| input-file | No | Path to the manifest file. When set, the tool will read ingresses from the file instead of reading from the cluster. Supported files are yaml and json. | |
| namespace | No | If present, the namespace scope for the invocation. | |
| output | yaml | No | The output format, either yaml or json. |
| providers | Yes | Comma-separated list of providers (only ingress-nginx is supported in this downstream). | |
| emitter | standard | No | The emitter to use for generating Gateway API resources (supported values: standard, kgateway). |
| kubeconfig | No | The kubeconfig file to use when talking to the cluster. If the flag is not set, a set of standard locations can be searched for an existing kubeconfig file. |
Conversion of Ingress resources to Gateway API
Processing Order and Conflicts
Ingress resources will be processed with a defined order to ensure deterministic generated Gateway API configuration. This should also determine precedence order of Ingress resources and routes in case of conflicts.
Ingress resources with the oldest creation timestamp will be sorted first and therefore given precedence. If creation timestamps are equal, then sorting will be done based on the namespace/name of the resources. If an Ingress rule conflicts with another (e.g. same path match but different backends) an error will be reported for the one that sorted later.
Since the Ingress v1 spec does not itself have a conflict resolution guide, we have adopted this one. These rules are similar to the Gateway API conflict resolution guidelines.
Ingress resource fields to Gateway API fields
Given a set of Ingress resources, ingress2gateway will generate a Gateway with
various HTTP and HTTPS Listeners as well as HTTPRoutes that should represent equivalent
routing rules.
| Ingress Field | Gateway API configuration |
|---|---|
ingressClassName |
If configured on an Ingress resource, this value will be translated to kgateway. |
defaultBackend |
If present, this configuration will generate a Gateway Listener with no hostname specified as well as a catchall HTTPRoute that references this listener. The backend specified here will be translated to a HTTPRoute rules[].backendRefs[] element. |
tls[].hosts |
Each host in an IngressTLS will result in a HTTPS Listener on the generated Gateway with the following: listeners[].hostname = host as described, listeners[].port = 443, listeners[].protocol = HTTPS, listeners[].tls.mode = Terminate |
tls[].secretName |
The secret specified here will be referenced in the Gateway HTTPS Listeners mentioned above with the field listeners[].tls.certificateRefs. Each Listener for each host in an IngressTLS will get this secret. |
rules[].host |
If non-empty, each distinct value for this field in the provided Ingress resources will result in a separate Gateway HTTP Listener with matching listeners[].hostname. listeners[].port will be set to 80 and listeners[].protocol set to HTTPS. In addition, Ingress rules with the same hostname will generate HTTPRoute rules in a HTTPRoute with hostnames containing it as the single element. If empty, similar to the defaultBackend, a Gateway Listener with no hostname configuration will be generated (if it doesn’t exist) and routing rules will be generated in a catchall HTTPRoute. |
rules[].http.paths[].path |
This field translates to a HTTPRoute rules[].matches[].path.value configuration. |
rules[].http.paths[].pathType |
This field translates to a HTTPRoute rules[].matches[].path.type configuration. Ingress Exact = HTTPRoute Exact match. Ingress Prefix = HTTPRoute PathPrefix match. |
rules[].http.paths[].backend |
The backend specified here will be translated to a HTTPRoute rules[].backendRefs[] element. |
Supported Annotations
Traffic Behavior
nginx.ingress.kubernetes.io/client-body-buffer-sizenginx.ingress.kubernetes.io/proxy-body-sizenginx.ingress.kubernetes.io/enable-corsnginx.ingress.kubernetes.io/cors-allow-originnginx.ingress.kubernetes.io/cors-allow-credentialsnginx.ingress.kubernetes.io/cors-allow-headersnginx.ingress.kubernetes.io/cors-expose-headersnginx.ingress.kubernetes.io/cors-allow-methodsnginx.ingress.kubernetes.io/cors-max-agenginx.ingress.kubernetes.io/limit-rpsnginx.ingress.kubernetes.io/limit-rpmnginx.ingress.kubernetes.io/limit-burst-multipliernginx.ingress.kubernetes.io/proxy-send-timeoutnginx.ingress.kubernetes.io/proxy-read-timeoutnginx.ingress.kubernetes.io/ssl-redirect: When set to"true", adds aRequestRedirectfilter to HTTPRoute rules that redirects HTTP to HTTPS with a 301 status code. Note that ingress-nginx redirects with code 308, but that isn’t supported by gateway API.nginx.ingress.kubernetes.io/force-ssl-redirect: When set to"true", adds aRequestRedirectfilter to HTTPRoute rules that redirects HTTP to HTTPS with a 301 status code. Treated identically tossl-redirect. Note that ingress-nginx redirects with code 308, but that isn’t supported by gateway API.nginx.ingress.kubernetes.io/ssl-passthrough: When set to"true", enables TLS passthrough mode. Converts the Ingress to aTLSRoutewith a Gateway listener usingprotocol: TLSandtls.mode: Passthrough. The HTTPRoute that would normally be created is removed.nginx.ingress.kubernetes.io/use-regex: When set to"true", indicates that the paths defined on an Ingress should be treated as regular expressions. Uses host-group semantics: if any Ingress contributing rules for a given host hasuse-regex: "true", regex-style path matching is enforced on all paths for that host (across all contributing Ingresses).nginx.ingress.kubernetes.io/rewrite-target: Rewrites the request path using regex rewrite semantics. Uses host-group semantics: if any Ingress contributing rules for a given host setsrewrite-target, regex-style path matching is enforced on all paths for that host (across all contributing Ingresses), consistent with ingress-nginx behavior.
Backend Behavior
nginx.ingress.kubernetes.io/proxy-connect-timeout: Sets the timeout for establishing a connection with a proxied server. It should be noted that this timeout cannot usually exceed 75 seconds.nginx.ingress.kubernetes.io/load-balance: Sets the algorithm to use for load balancing to a proxied server. The only supported value isround_robin.nginx.ingress.kubernetes.io/affinity: Enables session affinity (only “cookie” type is supported). Maps toBackendConfigPolicy.spec.loadBalancer.ringHash.hashPolicies.nginx.ingress.kubernetes.io/session-cookie-name: Specifies the name of the cookie used for session affinity. Maps toBackendConfigPolicy.spec.loadBalancer.ringHash.hashPolicies[].cookie.name.nginx.ingress.kubernetes.io/session-cookie-path: Defines the path that will be set on the cookie. Maps toBackendConfigPolicy.spec.loadBalancer.ringHash.hashPolicies[].cookie.path.- Note (regex-mode constraint): Ingress NGINX session cookie paths do not support regex. If regex-mode is enabled for a host (via
use-regex: "true"orrewrite-target) and cookie affinity is used,session-cookie-pathmust be set; the provider validates this and emits an error if it is missing. nginx.ingress.kubernetes.io/session-cookie-domain: Sets the Domain attribute of the sticky cookie. Note: This annotation is parsed but not currently mapped to kgateway as the Cookie type doesn’t support domain.nginx.ingress.kubernetes.io/session-cookie-samesite: Applies a SameSite attribute to the sticky cookie. Browser accepted values are None, Lax, and Strict. Maps toBackendConfigPolicy.spec.loadBalancer.ringHash.hashPolicies[].cookie.sameSite.nginx.ingress.kubernetes.io/session-cookie-expires: Sets the TTL/expiration time for the cookie. Maps toBackendConfigPolicy.spec.loadBalancer.ringHash.hashPolicies[].cookie.ttl.nginx.ingress.kubernetes.io/session-cookie-max-age: Sets the TTL/expiration time for the cookie (takes precedence oversession-cookie-expires). Maps toBackendConfigPolicy.spec.loadBalancer.ringHash.hashPolicies[].cookie.ttl.nginx.ingress.kubernetes.io/session-cookie-secure: Sets the Secure flag on the cookie. Maps toBackendConfigPolicy.spec.loadBalancer.ringHash.hashPolicies[].cookie.secure.nginx.ingress.kubernetes.io/service-upstream: When set to"true", configures Kgateway to route to the Service’s cluster IP (or equivalent static host) instead of individual Pod IPs. For each covered Service, the emitter creates aBackendresource withspec.type: Staticand rewrites the correspondingHTTPRoute.spec.rules[].backendRefs[]to reference thatBackend(groupgateway.kgateway.dev, kindBackend).nginx.ingress.kubernetes.io/backend-protocol: Indicates the L7 protocol that is used to communicate with the proxied backend.- Supported values (mapped):
GRPC,GRPCS- If
service-upstream: "true"is also set for the same Service backend, the emitter setsspec.static.appProtocol: grpcon the generatedBackend. - Otherwise, the emitter does not create or modify Kubernetes
Serviceresources. Instead, it emits an INFO notification with akubectl patchcommand to update the existing Service port withappProtocol: grpc.
- If
- Values treated as default HTTP/1.x (no-op):
HTTP,HTTPS,AUTO_HTTP - Unsupported values (rejected by provider):
FCGI(and others) - Safety note: Because emitting Service manifests could overwrite user-managed Service configuration, ingress2gateway intentionally avoids generating Service resources for this annotation.
- Supported values (mapped):
External Auth
nginx.ingress.kubernetes.io/auth-url: Specifies the URL of an external authentication service.nginx.ingress.kubernetes.io/auth-response-headers: Comma-separated list of headers to pass to backend once authentication request completes.
Basic Auth
nginx.ingress.kubernetes.io/auth-type: Must be set to"basic"to enable basic authentication. Maps toTrafficPolicy.spec.basicAuth.nginx.ingress.kubernetes.io/auth-secret: Specifies the secret containing basic auth credentials innamespace/nameformat (or justnameif in the same namespace). Maps toTrafficPolicy.spec.basicAuth.secretRef.name.nginx.ingress.kubernetes.io/auth-secret-type: Only"auth-file"is supported (default). The secret must contain an htpasswd file in the key"auth". Only SHA hashed passwords are supported. Maps toTrafficPolicy.spec.basicAuth.secretRef.keyset to"auth".
Backend TLS
nginx.ingress.kubernetes.io/proxy-ssl-secret: Maps toBackendConfigPolicy.spec.tls.secretRefnginx.ingress.kubernetes.io/proxy-ssl-verify: Maps toBackendConfigPolicy.spec.tls.insecureSkipVerify(inverted:"on"=false,"off"=true)nginx.ingress.kubernetes.io/proxy-ssl-name: Maps toBackendConfigPolicy.spec.tls.sni(automatically enables SNI)
Access Logging
nginx.ingress.kubernetes.io/enable-access-log: If enabled, will create an HTTPListenerPolicy that will configure a basic policy for envoy access logging. Maps toHTTPListenerPolicy.spec.accessLog[].fileSink. This can be further customized as needed, see docs.
Regex Path Matching and Rewrites
-
use-regexandrewrite-targetmay mutate HTTPRoute path matching for a host:- When regex-mode is enabled for a host, the emitter converts all
PathPrefix/Exactmatches under that host toRegularExpressionmatches. - For Ingresses that set
use-regex: "true", their contributed path strings are treated as regex (not escaped as literals). - For other Ingresses under the same host (that did not set
use-regex: "true"), their contributed path strings are treated as literals within a regex match (escaped), to preserve the original non-regex intent.
- When regex-mode is enabled for a host, the emitter converts all
-
rewrite-targetgeneratesTrafficPolicyURL rewrite:-
For each rule covered by an Ingress that sets
rewrite-target, the emitter creates a per-rule TrafficPolicy named:<ingress-name>-rewrite-<rule-index>
-
That policy sets:
spec: urlRewrite: pathRegex: pattern: <regex pattern derived from the HTTPRoute rule match> substitution: <rewrite-target value> -
The policy is attached via
ExtensionReffilters to only the covered backendRefs (partial coverage), rather than usingtargetRefs.
-
TrafficPolicy Projection
Annotations in the Traffic Behavior category are converted into
TrafficPolicy resources.
These policies are attached using:
targetRefswhen the policy applies to all backends, orextensionRefbackend filters for partial coverage.
Examples:
- Body size annotations control
spec.buffer.maxRequestSize - Rate limit annotations control
spec.rateLimit.local.tokenBucket - Timeout annotations control
spec.timeouts.requestorstreamIdle - SSL redirect annotations add
RequestRedirectfilters to HTTPRoute rules - SSL passthrough annotation converts HTTPRoute to TLSRoute with TLS passthrough Gateway listener
BackendConfigPolicy Projection
Annotations in the Backend Behavior category are converted into
BackendConfigPolicy resources.
Currently supported:
proxy-connect-timeout: Maps toBackendConfigPolicy.spec.connectTimeout- Session affinity annotations: Maps to
BackendConfigPolicy.spec.loadBalancer.ringHash.hashPolicieswith cookie-based hash policy
If multiple Ingresses target the same Service with conflicting proxy-connect-timeout values,
the lowest timeout wins and a warning is emitted.
TLSRoute Projection
Annotations that require TLS passthrough mode are converted into TLSRoute resources instead of HTTPRoute resources.
Currently supported:
nginx.ingress.kubernetes.io/ssl-passthrough:- When enabled, the Ingress is converted to a
TLSRouteresource - A Gateway listener is created with
protocol: TLSandtls.mode: Passthrough - The listener uses port 443 (when hostname is specified) or 8443 (default)
- The HTTPRoute that would normally be created is removed
- Backend services must handle TLS termination themselves
- When enabled, the Ingress is converted to a
Backend Projection
Annotations that change how upstreams are represented (rather than how they are load balanced or configured)
can be projected into Kgateway Backend resources.
Currently supported:
nginx.ingress.kubernetes.io/service-upstream:- For each Service backend covered by an Ingress with
service-upstream: "true", the emitter creates aBackendwith:spec.type: Staticspec.static.hostscontaining a single{host, port}entry derived from the Service (e.g.myservice.default.svc.cluster.local:80).
- Matching
HTTPRoute.spec.rules[].backendRefs[]are rewritten to reference thisBackendinstead of the core Service.
- For each Service backend covered by an Ingress with
nginx.ingress.kubernetes.io/backend-protocol:- When set to
GRPCorGRPCSandservice-upstream: "true"is set for the same backend, the emitter stampsspec.static.appProtocol: grpcon the generatedBackend. - When set to
GRPCorGRPCSwithoutservice-upstream: "true", the emitter emits an INFO notification that includes akubectl patch service ...command to setspec.ports[].appProtocolon the existing Service. HTTP,HTTPS, andAUTO_HTTPare treated as default HTTP/1.x behavior and do not emit additional config.
- When set to
Summary of Policy Types
| Annotation Type | Kgateway Resource |
|---|---|
| Request/response behavior | TrafficPolicy |
| Upstream connection behavior | BackendConfigPolicy |
| Upstream representation. | Backend |
| TLS passthrough | TLSRoute |
Limitations
- Only the ingress-nginx provider is currently supported by the Kgateway emitter.
- Some NGINX behaviors cannot be reproduced exactly due to Envoy/Kgateway differences.
- Regex-mode is implemented by converting HTTPRoute path matches to
RegularExpression. Some ingress-nginx details (such as case-insensitive~*behavior) may not be reproduced exactly depending on the underlying Gateway API/Envoy behavior and the patterns provided.
Supported but not translated Annotations
The following annotations have equivalents in kgateway but are not (as of yet) translated by this tool.
nginx.ingress.kubernetes.io/auth-proxy-set-headers
Supported in TrafficPolicy
spec:
extAuth:
httpService:
authorizationRequest:
headersToAdd:
- key: x-forwarded-host
value: "%DOWNSTREAM_REMOTE_ADDRESS%"