Repository: kubernetes/ingress-nginx (Go)

Issue:
strict-validate-path-type does not allow period/dot/. in Exact or Prefix path
**What happened**:

The documentation mentions the following text:

> When pathType is configured as Exact or Prefix, there should be a more strict validation, allowing only paths starting with "/" and containing only alphanumeric characters and "-", "_" and additional "/".

However the term `alphanumeric characters` is not specified and the term does not seem to be universally specified either. Some sources for example include special characters in `alphanumeric characters`, which seems to defeat the purpose of the strict checking.

The current text, depending on the definition of `alphanumeric characters`, seems to suggest that for example the period `.` character is not allowed when using Exact or Prefix. This would mean that a path of `https://example.com/index.html`, `https://example.com/.well-known/openid-configuration` or `https://example.com/.well-known/acme-challenge/3857265` would be invalid for path type Exact and Prefix. I cannot imagine that it's intentional that all these super common url's would be unsupported by Exact or Prefix.

**What you expected to happen**:

Define the term `alphanumeric characters` as used by this project and allow `.` in Exact and Prefix path types.

<!-- If this is actually about documentation, uncomment the following block -->

/kind documentation
/remove-kind bug


Discussion:
Then does this https://kubernetes.io/docs/concepts/overview/working-with-objects/names/ page also has the words `alphanumeric characters`
also https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/
![image](https://github.com/kubernetes/ingress-nginx/assets/5085914/539f3df7-c4a1-4cdd-9172-9157fe0e4b0f)

And I could be wrong, but `period/dot/.`  is known as a separator 
@longwuyuan You're linking documentation in the context of Kubernetes objects, but these requirements are not the same as used by ingress-nginx for path validation. This documentation does indeed mention that alphanumeric means `[a-z0-9A-Z]` in this context, but ingress-nginx does not define what alphanumeric means. If alphanumeric in the context of ingress-nginx means the same as the definition as defined in the documents you linked, then I think this should be mentioned in the docs for strict-validate-path-type.

I looked up the validation of the path as used by ingress-nginx here: https://github.com/kubernetes/ingress-nginx/blob/e44cab7245ad0e0c31cb6d0c947fa1aa51cff1ba/internal/ingress/inspector/rules.go#L37

> And I could be wrong, but period/dot/. is known as a separator

Be that as it may, this is not supported by the path validation in ingress-nginx so you can't even host static files with a file extenstion (index.html for example) using Exact or Prefix paths when `strict-validate-path-type` is enabled.

See: https://regex101.com/r/WjwI0c/1

I think this is an oversight and that `.` should be supported in Exact and Prefix path types. So actually perhaps this is not only a documentation issue but also either a bug or a feature request.
thanks @rouke-broersma , maybe we should keep adding thoughts here so it helps make progress.
- Could you paste a sample ingress manifest and the log messages for an attempt to create that sample 
- Would you opine that the suggested changes improve security or risk stays the same as now or risk is increased
@longwuyuan here is an ingress from my existing homelab:

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: bazarr-auth
  namespace: authentik
spec:
  rules:
    - host: bazarr.example.com
      http:
        paths:
          - backend:
              service:
                name: ak-outpost-authentik-embedded-outpost
                port:
                  number: 9000
            path: /outpost.goauthentik.io
            pathType: Prefix
```

Log message:

```
admission webhook "validate.nginx.ingress.kubernetes.io" denied the request: ingress contains invalid paths: path /outpost.goauthentik.io cannot be used with pathType Prefix
```

I would say that since `/` is required in the path and `period/dot/.` is not a special character when in the path that there is no increased security risk in allowing this character in `Exact` and `Prefix`.

I would say that allowing the commonly used `period/dot/.` in `Exact` and `Prefix` instead of only in `ImplementationSpecific` increases security because `ImplementationSpecific` is not validated but a user is now forced to use it.
- I see your path starts with `"forward slash"` char 
- I see docs saying paths starting with `"forward slash"` are allowed
![image](https://github.com/kubernetes/ingress-nginx/assets/5085914/3572f975-f9da-49fe-85b3-83fe028f455e)

@longwuyuan I know `/` is allowed. In fact `/` is required. I'm saying since `/` is required there is no risk in allowing `.`. If `/` was **not** required then allowing `.` _might_ be a risk, because a user could create a ingress like this:

```yaml
    - host: bazarr.example.com
      http:
        paths:
            path: .outpost.goauthentik.io
            pathType: Prefix
```

This _might_ change the host part instead of only the path part of the ingress, which could be a concern. However since `/` is required, this is **not** a concern.
I think I am trying to see enough text in this issue description here that settles the following thoughts 

- What precisely makes the use of phrase `"alphanumeric chars"` in this project's docs, go out of context, when this project is a sub of K8S sig-network, and K8S docs use the phrase and even explain the phrase clearly as screenshot pasted earlier

- For security sake, if a user is required to set pathType as `"ImplementationSpecific"`, to spec the `path` field with a `dot/./period/`  characters,  then  what is the problem created for the user. I have not seen many daily use URLs that contain a period character anyways and neither do people substantially often create subfolders/subdirectories with period/dot character in the name of the folder to be served over a browser or in api-calls

- There is another open issue where not allowing the dollar/$  char broke a working annotation, in production, so the project does ack improvements needed. And without feedack like this one, it would be impossible to improve/fix issues. So thank you very much.

-  But actionable tasks come out of elaborate clear data and that data comes out of triage and these discussions. There is an acute shortage of  developer time so data here helps reduce time a developer needs to research the issue
> What precisely makes the use of phrase "alphanumeric chars" in this project's docs, go out of context, when this project is a sub of K8S sig-network, and K8S docs use the phrase and even explain the phrase clearly as screenshot pasted earlier

Ingress-nginx implements it's own regex for the path validation, which is not actually exactly the same as the docs you linked. The ingress-nginx docs also do not link to this other documentation, so while one can make the assumption that this is a term shared within the k8s sig-network, it would be an improvement to the docs if it actually mentions this if this is in fact the case. As it stands there is no reason to assume both usages of the term are the same, since the ingress-nginx docs are also not hosted in the same location. You are more familiar with the kubernetes project so for you it makes sense that they are related. For me as an outsider it is not as obvious, so I turned to Google to tell me what alphanumerical characters means, and there I did not find one consistent definition. Some did include `.` and some did not.

> For security sake, if a user is required to set pathType as "ImplementationSpecific", to spec the path field with a dot/./period/ characters, then what is the problem created for the user. I have not seen many daily use URLs that contain a period character anyways and neither do people substantially often create subfolders/subdirectories with period/dot character in the name of the folder to be served over a browser or in api-calls

`ImplementationSpecific` allows special characters that are dangerous. A user is forced to choose a more dangerous, non-validated path type, to be able to use a non-dangerous character in the path. It is my opinion that this decreases security, because a user is more often forced to choose a less-secure method.

I disagree with you that the `.` is an uncommon character in the path of a url. Sure, these days it is also common to no longer include the file name and extension in paths, but there are many sites that still serve content where the file extension is in the path (index.html, index.php etc). File names an extensions are an integral part to serving web content.

On top of that the `/.well-known/` construct is also used a lot, for example in oidc and in the HTTP01 ACME TLS certificate challenge protocol: https://letsencrypt.org/docs/challenge-types/

> But actionable tasks come out of elaborate clear data and that data comes out of triage and these discussions. There is an acute shortage of developer time so data here helps reduce time a developer needs to research the issue

In this case I believe the actual change is very minor, it would only involve changing the validating regex I linked above and adding a test case.

That said I believe this discussion is currently highly relevant because `strict-validate-path-type` will be turned on by default in the next release, and I believe that many users will be caught off-guard by `.` being an invalid character since it's so integral to how webservers work. Bug reports from these users will also eat up valuable developer time, so imo either fixing this or alternatively explicitly mentioning in the docs that the `.` character is invalid for these path types will save a lot of time in the future.
Thank you for pointing this out; many recent changes were due to security issues with path validation and annotations. They were made quickly to prevent these issues. Please feel free to update the documentation to remind folks that they should be Implementation Specific if they have dots in their paths. 

/kind documentation
/good-first-issue
@strongjz: 
	This request has been marked as suitable for new contributors.

### Guidelines
Please ensure that the issue body includes answers to the following questions:
- Why are we solving this issue?
- To address this issue, are there any code changes? If there are code changes, what needs to be done in the code and what places can the assignee treat as reference points?
- Does this issue have zero to low barrier of entry?
- How can the assignee reach out to you for help?


For more details on the requirements of such an issue, please see [here](https://git.k8s.io/community/contributors/guide/help-wanted.md#good-first-issue) and ensure that they are met.

If this request no longer meets these requirements, the label can be removed
by commenting with the `/remove-good-first-issue` command.


<details>

In response to [this](https://github.com/kubernetes/ingress-nginx/issues/11176):

>Thank you for pointing this out; many recent changes were due to security issues with path validation and annotations. They were made quickly to prevent these issues. Please feel free to update the documentation to remind folks that they should be Implementation Specific if they have dots in their paths. 
>
>/kind documentation
>/good-first-issue


Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
/triage accepted
/priority backlog
@rouke-broersma are you going to open a PR to update the documentation around this? 


> @rouke-broersma are you going to open a PR to update the documentation around this?

I can do that. 

Just to be absolutely sure, the current behavior is as intended and changing this is not acceptable to the project? 

Changing the behavior would ultimately have my preference, because I consider the current behavior less secure from a user perspective
This is stale, but we won't close it automatically, just bare in mind the maintainers may be busy with other tasks and will reach your issue ASAP. If you have any question or request to prioritize this, please reach `#ingress-nginx-dev` on Kubernetes Slack.
For what it's worth, it seems the exclusion of `.` is intentional and related to kubernetes/kubernetes#126815 (CVE-2022-4886). The CVE workaround can be found in kubernetes/ingress-nginx#9967, which introduces this validation. I don't think there's a CVE-2022-4886 proof of concept available, so I'm not sure why `.` is a potentially evil character in this context (maybe Go templating qualifier?)
@rouke-broersma please only update the docs. 
so as the default changed, the synapse chart I was using has failed trying to add a path for /.well-known/matrix
https://gitlab.com/ananace/charts/-/blob/master/charts/matrix-synapse/templates/ingress.yaml?ref_type=heads#L180-191

It sounds like from this thread I should submit a PR that updates them to implementation specific, which disables validation right? Or is there still debate if periods are going to be allowed?
> so as the default changed, the synapse chart I was using has failed trying to add a path for /.well-known/matrix https://gitlab.com/ananace/charts/-/blob/master/charts/matrix-synapse/templates/ingress.yaml?ref_type=heads#L180-191
> 
> It sounds like from this thread I should submit a PR that updates them to implementation specific, which disables validation right? Or is there still debate if periods are going to be allowed?

Period is unfortunately not going to be allowed so you will have to change the path type to ImplementationSpecific.
I think @Gacko put in a PR to allow period and other chars in some places but (as @rouke-broersma poonted out) that pathType value required is `ImplementationSpecific`
> I think @Gacko put in a PR to allow period and other chars in some places but (as @rouke-broersma poonted out) that pathType value required is `ImplementationSpecific`

Could you link the pr? I could not find it
@Gacko hoping you can comment here about the PR in which you added dot/period and other characters as valid characters.
> Period is unfortunately not going to be allowed so you will have to change the path type to ImplementationSpecific.

Instead of fixing the validation regex so that it properly validates URL Paths (and `.` is definitely a valid char in URL paths) -- we are instead telling everyone to use `ImplementationSpecific` to bypass the controllers new strict URL path validations -- and this will somehow lead to better security?

https://www.ietf.org/rfc/rfc3986.txt
> 2.3.  Unreserved Characters
   Characters that are allowed in a URI but do not have a reserved
   purpose are called unreserved.  These include uppercase and lowercase
   letters, decimal digits, hyphen, **period**, underscore, and **tilde**.
      unreserved  = ALPHA / DIGIT / "-" / "." / "_" / "~"

It seems to me the original issue (CVE) is that the controller is treating the `path` as a regex string?  I am not seeing in the [docs](https://kubernetes.io/docs/concepts/services-networking/ingress/) where controllers are supposed to do that.  If people want to use regex defined paths, they should use ImplementationSpecific (and its great ingress-nginx provides that capability for people who want it).

However, Prefix/Exact pathTypes should be treated as literal (not regex) strings, and its validation should conform to the published RFCs.
I agree, which is why I reported this issue. 
So `.` is not allowed in `pathType: Exact` ingress rules?

My ingress rule:

```
          - path: /robots.txt
            pathType: Exact
            backend:
              service:
                name: my-service
                port:
                  number: 443
```

Gives me the error:

```
E0325 16:17:37.294535       7 main.go:96] "invalid ingress configuration" err=<
	ingress contains invalid paths: path /robots.txt cannot be used with pathType Exact
 > ingress="default/ingress-app"
```

and the workaround is 
```
            pathType: ImplementationSpecific

```

And this bug has been open for a year and nobody is fixing it?

Please, correct me where I'm wrong here?
So why exactly will `.` not be allowed in Prefix and Exact path types? If you are already validating that the paths start with `/` then surely a `.` is not a problem? This has broken a lot of our GRPC ingresses because Go GRPC prefixes the namespace before the Service name eg: `/ns.GreeterService/SayHello` (and there is no way to override that, afaik)
> So why exactly will `.` not be allowed in Prefix and Exact path types? If you are already validating that the paths start with `/` then surely a `.` is not a problem? This has broken a lot of our GRPC ingresses because Go GRPC prefixes the namespace before the Service name eg: `/ns.GreeterService/SayHello` (and there is no way to override that, afaik)

same.  all my gRPC ingresses broke in the same fashion, and was forced to move to `ImplementationSpecific` pathType (just for this one ingress controller, the other cloud-vendor specific controllers I use are staying on Exact or Prefix).

What bugs me is that the reported CVE-2022-4886 (that drove this change) is not clearly documented, what the attack vectors are/were, and whether everyone moving to `ImplementationSpecific` is safe or not.   From my perspective, everyone being forced to move to `ImplementationSpecific` is just a workaround to **bypass** the stricter path validation checks that were implemented to resolve that CVE.

I get the sense that the "do not allow `.` in paths" may have been a quick hack to resolve that high-impacting Critical CVE in a very short amount of time (to prevent a `../../etc/passwd` type of thing) -- but it feels like it is a bandaid on a deeper unresolved flaw that may still exist in the code -- and might still exist on the `ImplementationSpecific` pathTypes because that bypasses the stricter path validation checks.

Ultimately, I just want confirmation that workaround (`ImplementationSpecific` pathType) is safe to use.
In https://github.com/kubernetes/ingress-nginx/issues/11176#issuecomment-2758193528 @bgehman  wrote:
> Ultimately, I just want confirmation that workaround (ImplementationSpecific pathType) is safe to use.

According to the ingress-nginx documentation, `ImplementationSpecific` paths are treated as regular expressions and are not safe. 

https://github.com/kubernetes/ingress-nginx/blob/6338a3ac71f5b5e38971d81dd80a49ca51e563d4/docs/examples/openpolicyagent/README.md?plain=1#L6-L12

* https://kubernetes.github.io/ingress-nginx/examples/openpolicyagent/
 * https://github.com/kubernetes/ingress-nginx/issues/11013

AFAIKS ingress-nginx currently has no safe way to route a an exact URL path, such as `/robots.txt`, or `/.well-known/`, or a gRPC path like `/ns.GreeterService/SayHello`.

I would like to add some subjective commentary to this discussion.

What is the authority of standardisation in heterogenous computer networks like the internet and how do we implement these standards to make networking software interoperable in the internet?

1. We use a process in the internet that is called `Request for Comments` (RFC) by the `Internet Engineering Task Force` (IETF). A RFC that is put into `Standard` is a document that has been reviewed and reworked by experts worldwide. A `RFC Standard` is the **authority** of how computer systems must communicate with each other.
2. We use a process in the internet to publish `Common Vulnerabilities and Exposures` (CVE) to announce important security problems in computer systems, mostly software. Insofar in the internet, a CVE mostly is a problem of an insecure implementation in a software of a RFC Standard. The software should change it's implementation so it adheres to RFC Standards securely.

When we apply these principles to the specific problem in this issue, we can make the following conclusions:

1. `ingress-nginx` has to implement the RFC Standards to be interoperable with the internet. A relevant list in this context, but not finalized:
  1.1. [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986): `Uniform Resource Identifier (URI): Generic Syntax`
  1.2. [RFC 8615](https://www.rfc-editor.org/rfc/rfc8615): `Well-Known Uniform Resource Identifiers (URIs)`
2. `ingress-nginx` has to fix it's CVEs.

Which can be further simplified and concluded into;

`ingress-nginx` has to support URI paths that include dots `.` while not being security vulnerable by that. <ins>Refusing the handling of paths with dots is not a fix of a CVE</ins>, but a wrongful implementation of the internet standards in the `ingress-nginx` software, rendering it partly or fully in-operable with the rest of the internet.


---
As an additional and final remark: Neither Kubernetes, Kubernetes SIGs, ingress-nginx, the Linux Foundation or others are authoritative of internet standards. Solely the [IETF](https://www.ietf.org/) is accepted as that.

I've opened a [PR](https://github.com/kubernetes/ingress-nginx/pull/13798) to fix the issue.



Patch:
diff --git a/internal/ingress/inspector/rules.go b/internal/ingress/inspector/rules.go
index 8388efdd5b..fc9e49d571 100644
--- a/internal/ingress/inspector/rules.go
+++ b/internal/ingress/inspector/rules.go
@@ -28,13 +28,13 @@ var (
 	invalidSecretsDir     = regexp.MustCompile(`/var/run/secrets`)
 	invalidByLuaDirective = regexp.MustCompile(`.*_by_lua.*`)
 
-	// validPathType enforces alphanumeric, -, _ and / characters.
-	// The field (?i) turns this regex case insensitive
+	// validPathType enforces alphanumeric, -, _ , . and / characters.
+	// The field (?i) turns this regex case-insensitive
 	// The remaining regex says that the string must start with a "/" (^/)
-	// the group [[:alnum:]\_\-\/]* says that any amount of characters (A-Za-z0-9), _, - and /
+	// the group [[:alnum:]\_\-\/\.]* says that any amount of characters (A-Za-z0-9), _, - , . and /
 	// are accepted until the end of the line
 	// Nothing else is accepted.
-	validPathType = regexp.MustCompile(`(?i)^/[[:alnum:]\_\-/]*$`)
+	validPathType = regexp.MustCompile(`(?i)^/[[:alnum:]._\-/]*$`)
 
 	invalidRegex = []regexp.Regexp{}
 )


Test patch:
diff --git a/internal/ingress/inspector/inspector_test.go b/internal/ingress/inspector/inspector_test.go
index 4798ba8783..cf19aba7ff 100644
--- a/internal/ingress/inspector/inspector_test.go
+++ b/internal/ingress/inspector/inspector_test.go
@@ -44,10 +44,18 @@ var (
 									PathType: &prefix,
 									Path:     "/xpto/ab0/x_ss-9",
 								},
+								{
+									PathType: &prefix,
+									Path:     "/.well-known/acme-challenge/",
+								},
 								{
 									PathType: &exact,
 									Path:     "/bla/",
 								},
+								{
+									PathType: &exact,
+									Path:     "/.well-known/acme-challenge/",
+								},
 							},
 						},
 					},
@@ -101,6 +109,14 @@ var (
 									PathType: &prefix,
 									Path:     "/lala/xp\ntest",
 								},
+								{
+									PathType: &prefix,
+									Path:     "/$lala/test",
+								},
+								{
+									PathType: &prefix,
+									Path:     "#lala/test",
+								},
 								{
 									Path: "notvalidpathname-panics",
 								},
@@ -177,6 +193,8 @@ func TestValidatePathType(t *testing.T) {
 				aErr("xpto/lala", "Exact"),
 				aErr("/foo/bar/[a-z]{3}", "Prefix"),
 				aErr("/lala/xp\ntest", "Prefix"),
+				aErr("/$lala/test", "Prefix"),
+				aErr("#lala/test", "Prefix"),
 				aErr("notvalidpathname-panics", "<nil>"),
 			),
 		},