mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2025-02-03 13:21:51 +02:00
Request ID Logging (#1087)
* Add RequestID to the RequestScope * Expose RequestID to auth & request loggers * Use the RequestID in templated HTML pages * Allow customizing the RequestID header * Document new Request ID support * Add more cases to scope/requestID tests * Split Get vs Generate RequestID funtionality * Add {{.RequestID}} to the request logger tests * Move RequestID management to RequestScope * Use HTML escape instead of sanitization for Request ID rendering
This commit is contained in:
parent
4d9de06b1d
commit
c1267bb92d
@ -4,9 +4,9 @@
|
||||
|
||||
## Important Notes
|
||||
|
||||
|
||||
- [#1103](https://github.com/oauth2-proxy/oauth2-proxy/pull/1103) Upstream request signatures via `--signature-key` is
|
||||
deprecated. Support will be removed completely in v8.0.0.
|
||||
- [1087](https://github.com/oauth2-proxy/oauth2-proxy/pull/1087) The default logging templates have been updated to include {{.RequestID}}
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
@ -14,7 +14,8 @@
|
||||
|
||||
- [#1045](https://github.com/oauth2-proxy/oauth2-proxy/pull/1045) Ensure redirect URI always has a scheme (@JoelSpeed)
|
||||
- [#1103](https://github.com/oauth2-proxy/oauth2-proxy/pull/1103) Deprecate upstream request signatures (@NickMeves)
|
||||
- [#914](https://github.com/oauth2-proxy/oauth2-proxy/pull/914) Extract email from id_token for azure provider when oidc is configured
|
||||
- [1087](https://github.com/oauth2-proxy/oauth2-proxy/pull/1087) Support Request ID in logging (@NickMeves)
|
||||
- [#914](https://github.com/oauth2-proxy/oauth2-proxy/pull/914) Extract email from id_token for azure provider when oidc is configured (@weinong)
|
||||
- [#1047](https://github.com/oauth2-proxy/oauth2-proxy/pull/1047) Refactor HTTP Server and add ServerGroup to handle graceful shutdown of multiple servers (@JoelSpeed)
|
||||
- [#1070](https://github.com/oauth2-proxy/oauth2-proxy/pull/1070) Refactor logging middleware to middleware package (@NickMeves)
|
||||
- [#1064](https://github.com/oauth2-proxy/oauth2-proxy/pull/1064) Add support for setting groups on session when using basic auth (@stefansedich)
|
||||
|
@ -108,6 +108,7 @@ An example [oauth2-proxy.cfg](https://github.com/oauth2-proxy/oauth2-proxy/blob/
|
||||
| `--redis-sentinel-connection-urls` | string \| list | List of Redis sentinel connection URLs (e.g. `redis://HOST[:PORT]`). Used in conjunction with `--redis-use-sentinel` | |
|
||||
| `--redis-use-cluster` | bool | Connect to redis cluster. Must set `--redis-cluster-connection-urls` to use this feature | false |
|
||||
| `--redis-use-sentinel` | bool | Connect to redis via sentinels. Must set `--redis-sentinel-master-name` and `--redis-sentinel-connection-urls` to use this feature | false |
|
||||
| `--request-id-header` | string | Request header to use as the request ID in logging | X-Request-Id |
|
||||
| `--request-logging` | bool | Log requests | true |
|
||||
| `--request-logging-format` | string | Template for request log lines | see [Logging Configuration](#logging-configuration) |
|
||||
| `--resource` | string | The resource that is protected (Azure AD only) | |
|
||||
@ -184,7 +185,7 @@ Logging of requests to the `/ping` endpoint (or using `--ping-user-agent`) can b
|
||||
Authentication logs are logs which are guaranteed to contain a username or email address of a user attempting to authenticate. These logs are output by default in the below format:
|
||||
|
||||
```
|
||||
<REMOTE_ADDRESS> - <user@domain.com> [19/Mar/2015:17:20:19 -0400] [<STATUS>] <MESSAGE>
|
||||
<REMOTE_ADDRESS> - <REQUEST ID> - <user@domain.com> [19/Mar/2015:17:20:19 -0400] [<STATUS>] <MESSAGE>
|
||||
```
|
||||
|
||||
The status block will contain one of the below strings:
|
||||
@ -197,7 +198,7 @@ If you require a different format than that, you can configure it with the `--au
|
||||
The default format is configured as follows:
|
||||
|
||||
```
|
||||
{{.Client}} - {{.Username}} [{{.Timestamp}}] [{{.Status}}] {{.Message}}
|
||||
{{.Client}} - {{.RequestID}} - {{.Username}} [{{.Timestamp}}] [{{.Status}}] {{.Message}}
|
||||
```
|
||||
|
||||
Available variables for auth logging:
|
||||
@ -206,26 +207,27 @@ Available variables for auth logging:
|
||||
| --- | --- | --- |
|
||||
| Client | 74.125.224.72 | The client/remote IP address. Will use the X-Real-IP header it if exists & reverse-proxy is set to true. |
|
||||
| Host | domain.com | The value of the Host header. |
|
||||
| Message | Authenticated via OAuth2 | The details of the auth attempt. |
|
||||
| Protocol | HTTP/1.0 | The request protocol. |
|
||||
| RequestID | 00010203-0405-4607-8809-0a0b0c0d0e0f | The request ID pulled from the `--request-id-header`. Random UUID if empty |
|
||||
| RequestMethod | GET | The request method. |
|
||||
| Timestamp | 19/Mar/2015:17:20:19 -0400 | The date and time of the logging event. |
|
||||
| UserAgent | - | The full user agent as reported by the requesting client. |
|
||||
| Username | username@email.com | The email or username of the auth request. |
|
||||
| Status | AuthSuccess | The status of the auth request. See above for details. |
|
||||
| Message | Authenticated via OAuth2 | The details of the auth attempt. |
|
||||
|
||||
### Request Log Format
|
||||
HTTP request logs will output by default in the below format:
|
||||
|
||||
```
|
||||
<REMOTE_ADDRESS> - <user@domain.com> [19/Mar/2015:17:20:19 -0400] <HOST_HEADER> GET <UPSTREAM_HOST> "/path/" HTTP/1.1 "<USER_AGENT>" <RESPONSE_CODE> <RESPONSE_BYTES> <REQUEST_DURATION>
|
||||
<REMOTE_ADDRESS> - <REQUEST ID> - <user@domain.com> [19/Mar/2015:17:20:19 -0400] <HOST_HEADER> GET <UPSTREAM_HOST> "/path/" HTTP/1.1 "<USER_AGENT>" <RESPONSE_CODE> <RESPONSE_BYTES> <REQUEST_DURATION>
|
||||
```
|
||||
|
||||
If you require a different format than that, you can configure it with the `--request-logging-format` flag.
|
||||
The default format is configured as follows:
|
||||
|
||||
```
|
||||
{{.Client}} - {{.Username}} [{{.Timestamp}}] {{.Host}} {{.RequestMethod}} {{.Upstream}} {{.RequestURI}} {{.Protocol}} {{.UserAgent}} {{.StatusCode}} {{.ResponseSize}} {{.RequestDuration}}
|
||||
{{.Client}} - {{.RequestID}} - {{.Username}} [{{.Timestamp}}] {{.Host}} {{.RequestMethod}} {{.Upstream}} {{.RequestURI}} {{.Protocol}} {{.UserAgent}} {{.StatusCode}} {{.ResponseSize}} {{.RequestDuration}}
|
||||
```
|
||||
|
||||
Available variables for request logging:
|
||||
@ -236,6 +238,7 @@ Available variables for request logging:
|
||||
| Host | domain.com | The value of the Host header. |
|
||||
| Protocol | HTTP/1.0 | The request protocol. |
|
||||
| RequestDuration | 0.001 | The time in seconds that a request took to process. |
|
||||
| RequestID | 00010203-0405-4607-8809-0a0b0c0d0e0f | The request ID pulled from the `--request-id-header`. Random UUID if empty |
|
||||
| RequestMethod | GET | The request method. |
|
||||
| RequestURI | "/oauth2/auth" | The URI path of the request. |
|
||||
| ResponseSize | 12 | The size in bytes of the response. |
|
||||
|
1
go.mod
1
go.mod
@ -13,6 +13,7 @@ require (
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32
|
||||
github.com/go-redis/redis/v8 v8.2.3
|
||||
github.com/google/uuid v1.2.0
|
||||
github.com/justinas/alice v1.2.0
|
||||
github.com/mbland/hmacauth v0.0.0-20170912233209-44256dfd4bfa
|
||||
github.com/mitchellh/mapstructure v1.1.2
|
||||
|
15
go.sum
15
go.sum
@ -39,7 +39,6 @@ github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQ
|
||||
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
@ -103,7 +102,6 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew=
|
||||
github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I=
|
||||
@ -149,7 +147,6 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
@ -171,8 +168,9 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
@ -204,7 +202,6 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
|
||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
@ -335,7 +332,6 @@ github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAm
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
|
||||
@ -345,14 +341,12 @@ github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66Id
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
|
||||
@ -361,7 +355,6 @@ github.com/prometheus/common v0.15.0 h1:4fgOnadei3EZvgRwxJ7RMpG1k1pOZth5Pc13tysp
|
||||
github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
@ -396,7 +389,6 @@ github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
@ -437,7 +429,6 @@ go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
@ -453,7 +444,6 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@ -506,7 +496,6 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -289,7 +289,7 @@ func (p *OAuthProxy) setupServer(opts *options.Options) error {
|
||||
// the OAuth2 Proxy authentication logic kicks in.
|
||||
// For example forcing HTTPS or health checks.
|
||||
func buildPreAuthChain(opts *options.Options) (alice.Chain, error) {
|
||||
chain := alice.New(middleware.NewScope(opts.ReverseProxy))
|
||||
chain := alice.New(middleware.NewScope(opts.ReverseProxy, opts.Logging.RequestIDHeader))
|
||||
|
||||
if opts.ForceHTTPS {
|
||||
_, httpsPort, err := net.SplitHostPort(opts.Server.SecureBindAddress)
|
||||
@ -596,7 +596,14 @@ func (p *OAuthProxy) ErrorPage(rw http.ResponseWriter, req *http.Request, code i
|
||||
redirectURL = "/"
|
||||
}
|
||||
|
||||
p.pageWriter.WriteErrorPage(rw, code, redirectURL, appError, messages...)
|
||||
scope := middlewareapi.GetRequestScope(req)
|
||||
p.pageWriter.WriteErrorPage(rw, pagewriter.ErrorPageOpts{
|
||||
Status: code,
|
||||
RedirectURL: redirectURL,
|
||||
RequestID: scope.RequestID,
|
||||
AppError: appError,
|
||||
Messages: messages,
|
||||
})
|
||||
}
|
||||
|
||||
// IsAllowedRequest is used to check if auth should be skipped for this request
|
||||
@ -657,7 +664,7 @@ func (p *OAuthProxy) SignInPage(rw http.ResponseWriter, req *http.Request, code
|
||||
redirectURL = "/"
|
||||
}
|
||||
|
||||
p.pageWriter.WriteSignInPage(rw, redirectURL)
|
||||
p.pageWriter.WriteSignInPage(rw, req, redirectURL)
|
||||
}
|
||||
|
||||
// ManualSignIn handles basic auth logins to the proxy
|
||||
|
@ -21,6 +21,10 @@ type RequestScope struct {
|
||||
// mode and if request `X-Forwarded-*` headers should be trusted
|
||||
ReverseProxy bool
|
||||
|
||||
// RequestID is set to the request's `X-Request-Id` header if set.
|
||||
// Otherwise a random UUID is set.
|
||||
RequestID string
|
||||
|
||||
// Session details the authenticated users information (if it exists).
|
||||
Session *sessions.SessionState
|
||||
|
||||
|
@ -17,6 +17,7 @@ type Logging struct {
|
||||
ExcludePaths []string `flag:"exclude-logging-path" cfg:"exclude_logging_paths"`
|
||||
LocalTime bool `flag:"logging-local-time" cfg:"logging_local_time"`
|
||||
SilencePing bool `flag:"silence-ping-logging" cfg:"silence_ping_logging"`
|
||||
RequestIDHeader string `flag:"request-id-header" cfg:"request_id_header"`
|
||||
File LogFileOptions `cfg:",squash"`
|
||||
}
|
||||
|
||||
@ -43,6 +44,7 @@ func loggingFlagSet() *pflag.FlagSet {
|
||||
flagSet.StringSlice("exclude-logging-path", []string{}, "Exclude logging requests to paths (eg: '/path1,/path2,/path3')")
|
||||
flagSet.Bool("logging-local-time", true, "If the time in log files and backup filenames are local or UTC time")
|
||||
flagSet.Bool("silence-ping-logging", false, "Disable logging of requests to ping endpoint")
|
||||
flagSet.String("request-id-header", "X-Request-Id", "Request header to use as the request ID")
|
||||
|
||||
flagSet.String("logging-filename", "", "File to log requests to, empty for stdout")
|
||||
flagSet.Int("logging-max-size", 100, "Maximum size in megabytes of the log file before rotation")
|
||||
@ -59,6 +61,7 @@ func loggingDefaults() Logging {
|
||||
ExcludePaths: nil,
|
||||
LocalTime: true,
|
||||
SilencePing: false,
|
||||
RequestIDHeader: "X-Request-Id",
|
||||
AuthEnabled: true,
|
||||
AuthFormat: logger.DefaultAuthLoggingFormat,
|
||||
RequestEnabled: true,
|
||||
|
@ -47,7 +47,7 @@
|
||||
<h1 class="subtitle is-1">{{.Title}}</h1>
|
||||
</div>
|
||||
|
||||
{{ if .Message }}
|
||||
{{ if or .Message .RequestID }}
|
||||
<div id="more-info" class="block card is-fullwidth is-shadowless">
|
||||
<header class="card-header is-shadowless">
|
||||
<p class="card-header-title">More Info</p>
|
||||
@ -56,9 +56,16 @@
|
||||
</a>
|
||||
</header>
|
||||
<div class="card-content has-text-left is-hidden">
|
||||
{{ if .Message }}
|
||||
<div class="content">
|
||||
{{.Message}}
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ if .RequestID }}
|
||||
<div class="content">
|
||||
Request ID: {{.RequestID}}
|
||||
</div>
|
||||
{{ end }}
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
|
||||
)
|
||||
|
||||
@ -37,11 +38,25 @@ type errorPageWriter struct {
|
||||
debug bool
|
||||
}
|
||||
|
||||
// ErrorPageOpts bundles up all the content needed to write the Error Page
|
||||
type ErrorPageOpts struct {
|
||||
// HTTP status code
|
||||
Status int
|
||||
// Redirect URL for "Go back" and "Sign in" buttons
|
||||
RedirectURL string
|
||||
// The UUID of the request
|
||||
RequestID string
|
||||
// App Error shown in debug mode
|
||||
AppError string
|
||||
// Generic error messages shown in non-debug mode
|
||||
Messages []interface{}
|
||||
}
|
||||
|
||||
// WriteErrorPage writes an error page to the given response writer.
|
||||
// It uses the passed redirectURL to give users the option to go back to where
|
||||
// they originally came from or try signing in again.
|
||||
func (e *errorPageWriter) WriteErrorPage(rw http.ResponseWriter, status int, redirectURL string, appError string, messages ...interface{}) {
|
||||
rw.WriteHeader(status)
|
||||
func (e *errorPageWriter) WriteErrorPage(rw http.ResponseWriter, opts ErrorPageOpts) {
|
||||
rw.WriteHeader(opts.Status)
|
||||
|
||||
// We allow unescaped template.HTML since it is user configured options
|
||||
/* #nosec G203 */
|
||||
@ -51,14 +66,16 @@ func (e *errorPageWriter) WriteErrorPage(rw http.ResponseWriter, status int, red
|
||||
ProxyPrefix string
|
||||
StatusCode int
|
||||
Redirect string
|
||||
RequestID string
|
||||
Footer template.HTML
|
||||
Version string
|
||||
}{
|
||||
Title: http.StatusText(status),
|
||||
Message: e.getMessage(status, appError, messages...),
|
||||
Title: http.StatusText(opts.Status),
|
||||
Message: e.getMessage(opts.Status, opts.AppError, opts.Messages...),
|
||||
ProxyPrefix: e.proxyPrefix,
|
||||
StatusCode: status,
|
||||
Redirect: redirectURL,
|
||||
StatusCode: opts.Status,
|
||||
Redirect: opts.RedirectURL,
|
||||
RequestID: opts.RequestID,
|
||||
Footer: template.HTML(e.footer),
|
||||
Version: e.version,
|
||||
}
|
||||
@ -74,7 +91,14 @@ func (e *errorPageWriter) WriteErrorPage(rw http.ResponseWriter, status int, red
|
||||
// It is expected to always render a bad gateway error.
|
||||
func (e *errorPageWriter) ProxyErrorHandler(rw http.ResponseWriter, req *http.Request, proxyErr error) {
|
||||
logger.Errorf("Error proxying to upstream server: %v", proxyErr)
|
||||
e.WriteErrorPage(rw, http.StatusBadGateway, "", proxyErr.Error(), "There was a problem connecting to the upstream server.")
|
||||
scope := middlewareapi.GetRequestScope(req)
|
||||
e.WriteErrorPage(rw, ErrorPageOpts{
|
||||
Status: http.StatusBadGateway,
|
||||
RedirectURL: "", // The user is already logged in and has hit an upstream error. Makes no sense to redirect in this case.
|
||||
RequestID: scope.RequestID,
|
||||
AppError: proxyErr.Error(),
|
||||
Messages: []interface{}{"There was a problem connecting to the upstream server."},
|
||||
})
|
||||
}
|
||||
|
||||
// getMessage creates the message for the template parameters.
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"io/ioutil"
|
||||
"net/http/httptest"
|
||||
|
||||
middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
@ -14,7 +15,7 @@ var _ = Describe("Error Page Writer", func() {
|
||||
var errorPage *errorPageWriter
|
||||
|
||||
BeforeEach(func() {
|
||||
tmpl, err := template.New("").Parse("{{.Title}} {{.Message}} {{.ProxyPrefix}} {{.StatusCode}} {{.Redirect}} {{.Footer}} {{.Version}}")
|
||||
tmpl, err := template.New("").Parse("{{.Title}} {{.Message}} {{.ProxyPrefix}} {{.StatusCode}} {{.Redirect}} {{.RequestID}} {{.Footer}} {{.Version}}")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
errorPage = &errorPageWriter{
|
||||
@ -28,41 +29,77 @@ var _ = Describe("Error Page Writer", func() {
|
||||
Context("WriteErrorPage", func() {
|
||||
It("Writes the template to the response writer", func() {
|
||||
recorder := httptest.NewRecorder()
|
||||
errorPage.WriteErrorPage(recorder, 403, "/redirect", "Access Denied")
|
||||
errorPage.WriteErrorPage(recorder, ErrorPageOpts{
|
||||
Status: 403,
|
||||
RedirectURL: "/redirect",
|
||||
RequestID: testRequestID,
|
||||
AppError: "Access Denied",
|
||||
})
|
||||
|
||||
body, err := ioutil.ReadAll(recorder.Result().Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(string(body)).To(Equal("Forbidden You do not have permission to access this resource. /prefix/ 403 /redirect Custom Footer Text v0.0.0-test"))
|
||||
Expect(string(body)).To(Equal("Forbidden You do not have permission to access this resource. /prefix/ 403 /redirect 11111111-2222-4333-8444-555555555555 Custom Footer Text v0.0.0-test"))
|
||||
})
|
||||
|
||||
It("With a different code, uses the stock message for the correct code", func() {
|
||||
recorder := httptest.NewRecorder()
|
||||
errorPage.WriteErrorPage(recorder, 500, "/redirect", "Access Denied")
|
||||
errorPage.WriteErrorPage(recorder, ErrorPageOpts{
|
||||
Status: 500,
|
||||
RedirectURL: "/redirect",
|
||||
RequestID: testRequestID,
|
||||
AppError: "Access Denied",
|
||||
})
|
||||
|
||||
body, err := ioutil.ReadAll(recorder.Result().Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(string(body)).To(Equal("Internal Server Error Oops! Something went wrong. For more information contact your server administrator. /prefix/ 500 /redirect Custom Footer Text v0.0.0-test"))
|
||||
Expect(string(body)).To(Equal("Internal Server Error Oops! Something went wrong. For more information contact your server administrator. /prefix/ 500 /redirect 11111111-2222-4333-8444-555555555555 Custom Footer Text v0.0.0-test"))
|
||||
})
|
||||
|
||||
It("With a message override, uses the message", func() {
|
||||
recorder := httptest.NewRecorder()
|
||||
errorPage.WriteErrorPage(recorder, 403, "/redirect", "Access Denied", "An extra message: %s", "with more context.")
|
||||
errorPage.WriteErrorPage(recorder, ErrorPageOpts{
|
||||
Status: 403,
|
||||
RedirectURL: "/redirect",
|
||||
RequestID: testRequestID,
|
||||
AppError: "Access Denied",
|
||||
Messages: []interface{}{
|
||||
"An extra message: %s",
|
||||
"with more context.",
|
||||
},
|
||||
})
|
||||
|
||||
body, err := ioutil.ReadAll(recorder.Result().Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(string(body)).To(Equal("Forbidden An extra message: with more context. /prefix/ 403 /redirect Custom Footer Text v0.0.0-test"))
|
||||
Expect(string(body)).To(Equal("Forbidden An extra message: with more context. /prefix/ 403 /redirect 11111111-2222-4333-8444-555555555555 Custom Footer Text v0.0.0-test"))
|
||||
})
|
||||
|
||||
It("Sanitizes malicious user input", func() {
|
||||
recorder := httptest.NewRecorder()
|
||||
errorPage.WriteErrorPage(recorder, ErrorPageOpts{
|
||||
Status: 403,
|
||||
RedirectURL: "/redirect",
|
||||
RequestID: "<script>alert(1)</script>",
|
||||
AppError: "Access Denied",
|
||||
})
|
||||
|
||||
body, err := ioutil.ReadAll(recorder.Result().Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(string(body)).To(Equal("Forbidden You do not have permission to access this resource. /prefix/ 403 /redirect <script>alert(1)</script> Custom Footer Text v0.0.0-test"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("ProxyErrorHandler", func() {
|
||||
It("Writes a bad gateway error the response writer", func() {
|
||||
req := httptest.NewRequest("", "/bad-gateway", nil)
|
||||
req = middlewareapi.AddRequestScope(req, &middlewareapi.RequestScope{
|
||||
RequestID: testRequestID,
|
||||
})
|
||||
recorder := httptest.NewRecorder()
|
||||
errorPage.ProxyErrorHandler(recorder, req, errors.New("some upstream error"))
|
||||
|
||||
body, err := ioutil.ReadAll(recorder.Result().Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(string(body)).To(Equal("Bad Gateway There was a problem connecting to the upstream server. /prefix/ 502 Custom Footer Text v0.0.0-test"))
|
||||
Expect(string(body)).To(Equal("Bad Gateway There was a problem connecting to the upstream server. /prefix/ 502 11111111-2222-4333-8444-555555555555 Custom Footer Text v0.0.0-test"))
|
||||
})
|
||||
})
|
||||
|
||||
@ -78,7 +115,11 @@ var _ = Describe("Error Page Writer", func() {
|
||||
Context("WriteErrorPage", func() {
|
||||
It("Writes the detailed error in place of the message", func() {
|
||||
recorder := httptest.NewRecorder()
|
||||
errorPage.WriteErrorPage(recorder, 403, "/redirect", "Debug error")
|
||||
errorPage.WriteErrorPage(recorder, ErrorPageOpts{
|
||||
Status: 403,
|
||||
RedirectURL: "/redirect",
|
||||
AppError: "Debug error",
|
||||
})
|
||||
|
||||
body, err := ioutil.ReadAll(recorder.Result().Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
@ -89,6 +130,9 @@ var _ = Describe("Error Page Writer", func() {
|
||||
Context("ProxyErrorHandler", func() {
|
||||
It("Writes a bad gateway error the response writer", func() {
|
||||
req := httptest.NewRequest("", "/bad-gateway", nil)
|
||||
req = middlewareapi.AddRequestScope(req, &middlewareapi.RequestScope{
|
||||
RequestID: testRequestID,
|
||||
})
|
||||
recorder := httptest.NewRecorder()
|
||||
errorPage.ProxyErrorHandler(recorder, req, errors.New("some upstream error"))
|
||||
|
||||
|
@ -10,8 +10,8 @@ import (
|
||||
// It can also be used to write errors for the http.ReverseProxy used in the
|
||||
// upstream package.
|
||||
type Writer interface {
|
||||
WriteSignInPage(rw http.ResponseWriter, redirectURL string)
|
||||
WriteErrorPage(rw http.ResponseWriter, status int, redirectURL string, appError string, messages ...interface{})
|
||||
WriteSignInPage(rw http.ResponseWriter, req *http.Request, redirectURL string)
|
||||
WriteErrorPage(rw http.ResponseWriter, opts ErrorPageOpts)
|
||||
ProxyErrorHandler(rw http.ResponseWriter, req *http.Request, proxyErr error)
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,8 @@ import (
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
const testRequestID = "11111111-2222-4333-8444-555555555555"
|
||||
|
||||
func TestOptionsSuite(t *testing.T) {
|
||||
logger.SetOutput(GinkgoWriter)
|
||||
logger.SetErrOutput(GinkgoWriter)
|
||||
|
@ -2,6 +2,7 @@ package pagewriter
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -14,6 +15,7 @@ var _ = Describe("Writer", func() {
|
||||
Context("NewWriter", func() {
|
||||
var writer Writer
|
||||
var opts Opts
|
||||
var request *http.Request
|
||||
|
||||
BeforeEach(func() {
|
||||
opts = Opts{
|
||||
@ -26,6 +28,8 @@ var _ = Describe("Writer", func() {
|
||||
ProviderName: "<ProviderName>",
|
||||
SignInMessage: "<SignInMessage>",
|
||||
}
|
||||
|
||||
request = httptest.NewRequest("", "http://127.0.0.1/", nil)
|
||||
})
|
||||
|
||||
Context("With no custom templates", func() {
|
||||
@ -37,7 +41,11 @@ var _ = Describe("Writer", func() {
|
||||
|
||||
It("Writes the default error template", func() {
|
||||
recorder := httptest.NewRecorder()
|
||||
writer.WriteErrorPage(recorder, 500, "/redirect", "Some debug error")
|
||||
writer.WriteErrorPage(recorder, ErrorPageOpts{
|
||||
Status: 500,
|
||||
RedirectURL: "/redirect",
|
||||
AppError: "Some debug error",
|
||||
})
|
||||
|
||||
body, err := ioutil.ReadAll(recorder.Result().Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
@ -46,7 +54,7 @@ var _ = Describe("Writer", func() {
|
||||
|
||||
It("Writes the default sign in template", func() {
|
||||
recorder := httptest.NewRecorder()
|
||||
writer.WriteSignInPage(recorder, "/redirect")
|
||||
writer.WriteSignInPage(recorder, request, "/redirect")
|
||||
|
||||
body, err := ioutil.ReadAll(recorder.Result().Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
@ -80,7 +88,11 @@ var _ = Describe("Writer", func() {
|
||||
|
||||
It("Writes the custom error template", func() {
|
||||
recorder := httptest.NewRecorder()
|
||||
writer.WriteErrorPage(recorder, 500, "/redirect", "Some debug error")
|
||||
writer.WriteErrorPage(recorder, ErrorPageOpts{
|
||||
Status: 500,
|
||||
RedirectURL: "/redirect",
|
||||
AppError: "Some debug error",
|
||||
})
|
||||
|
||||
body, err := ioutil.ReadAll(recorder.Result().Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
@ -89,7 +101,7 @@ var _ = Describe("Writer", func() {
|
||||
|
||||
It("Writes the custom sign in template", func() {
|
||||
recorder := httptest.NewRecorder()
|
||||
writer.WriteSignInPage(recorder, "/redirect")
|
||||
writer.WriteSignInPage(recorder, request, "/redirect")
|
||||
|
||||
body, err := ioutil.ReadAll(recorder.Result().Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
|
||||
)
|
||||
|
||||
@ -53,7 +54,7 @@ type signInPageWriter struct {
|
||||
|
||||
// WriteSignInPage writes the sign-in page to the given response writer.
|
||||
// It uses the redirectURL to be able to set the final destination for the user post login.
|
||||
func (s *signInPageWriter) WriteSignInPage(rw http.ResponseWriter, redirectURL string) {
|
||||
func (s *signInPageWriter) WriteSignInPage(rw http.ResponseWriter, req *http.Request, redirectURL string) {
|
||||
// We allow unescaped template.HTML since it is user configured options
|
||||
/* #nosec G203 */
|
||||
t := struct {
|
||||
@ -79,7 +80,13 @@ func (s *signInPageWriter) WriteSignInPage(rw http.ResponseWriter, redirectURL s
|
||||
err := s.template.Execute(rw, t)
|
||||
if err != nil {
|
||||
logger.Printf("Error rendering sign-in template: %v", err)
|
||||
s.errorPageWriter.WriteErrorPage(rw, http.StatusInternalServerError, redirectURL, err.Error())
|
||||
scope := middlewareapi.GetRequestScope(req)
|
||||
s.errorPageWriter.WriteErrorPage(rw, ErrorPageOpts{
|
||||
Status: http.StatusInternalServerError,
|
||||
RedirectURL: redirectURL,
|
||||
RequestID: scope.RequestID,
|
||||
AppError: err.Error(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,11 +5,13 @@ import (
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/extensions/table"
|
||||
. "github.com/onsi/gomega"
|
||||
@ -18,10 +20,11 @@ import (
|
||||
var _ = Describe("SignIn Page", func() {
|
||||
|
||||
Context("SignIn Page Writer", func() {
|
||||
var request *http.Request
|
||||
var signInPage *signInPageWriter
|
||||
|
||||
BeforeEach(func() {
|
||||
errorTmpl, err := template.New("").Parse("{{.Title}}")
|
||||
errorTmpl, err := template.New("").Parse("{{.Title}} | {{.RequestID}}")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
errorPage := &errorPageWriter{
|
||||
template: errorTmpl,
|
||||
@ -41,12 +44,17 @@ var _ = Describe("SignIn Page", func() {
|
||||
displayLoginForm: true,
|
||||
logoData: "Logo Data",
|
||||
}
|
||||
|
||||
request = httptest.NewRequest("", "http://127.0.0.1/", nil)
|
||||
request = middlewareapi.AddRequestScope(request, &middlewareapi.RequestScope{
|
||||
RequestID: testRequestID,
|
||||
})
|
||||
})
|
||||
|
||||
Context("WriteSignInPage", func() {
|
||||
It("Writes the template to the response writer", func() {
|
||||
recorder := httptest.NewRecorder()
|
||||
signInPage.WriteSignInPage(recorder, "/redirect")
|
||||
signInPage.WriteSignInPage(recorder, request, "/redirect")
|
||||
|
||||
body, err := ioutil.ReadAll(recorder.Result().Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
@ -60,11 +68,11 @@ var _ = Describe("SignIn Page", func() {
|
||||
signInPage.template = tmpl
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
signInPage.WriteSignInPage(recorder, "/redirect")
|
||||
signInPage.WriteSignInPage(recorder, request, "/redirect")
|
||||
|
||||
body, err := ioutil.ReadAll(recorder.Result().Body)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(string(body)).To(Equal("Internal Server Error"))
|
||||
Expect(string(body)).To(Equal(fmt.Sprintf("Internal Server Error | %s", testRequestID)))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -51,6 +51,7 @@ var _ = Describe("Templates", func() {
|
||||
StatusCode int
|
||||
Title string
|
||||
Message string
|
||||
RequestID string
|
||||
|
||||
// For custom templates
|
||||
TestString string
|
||||
@ -67,6 +68,7 @@ var _ = Describe("Templates", func() {
|
||||
StatusCode: 404,
|
||||
Title: "<title>",
|
||||
Message: "<message>",
|
||||
RequestID: "<request-id>",
|
||||
|
||||
TestString: "Testing",
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
|
||||
requestutil "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests/util"
|
||||
)
|
||||
|
||||
@ -25,9 +26,9 @@ const (
|
||||
// DefaultStandardLoggingFormat defines the default standard log format
|
||||
DefaultStandardLoggingFormat = "[{{.Timestamp}}] [{{.File}}] {{.Message}}"
|
||||
// DefaultAuthLoggingFormat defines the default auth log format
|
||||
DefaultAuthLoggingFormat = "{{.Client}} - {{.Username}} [{{.Timestamp}}] [{{.Status}}] {{.Message}}"
|
||||
DefaultAuthLoggingFormat = "{{.Client}} - {{.RequestID}} - {{.Username}} [{{.Timestamp}}] [{{.Status}}] {{.Message}}"
|
||||
// DefaultRequestLoggingFormat defines the default request log format
|
||||
DefaultRequestLoggingFormat = "{{.Client}} - {{.Username}} [{{.Timestamp}}] {{.Host}} {{.RequestMethod}} {{.Upstream}} {{.RequestURI}} {{.Protocol}} {{.UserAgent}} {{.StatusCode}} {{.ResponseSize}} {{.RequestDuration}}"
|
||||
DefaultRequestLoggingFormat = "{{.Client}} - {{.RequestID}} - {{.Username}} [{{.Timestamp}}] {{.Host}} {{.RequestMethod}} {{.Upstream}} {{.RequestURI}} {{.Protocol}} {{.UserAgent}} {{.StatusCode}} {{.ResponseSize}} {{.RequestDuration}}"
|
||||
|
||||
// AuthSuccess indicates that an auth attempt has succeeded explicitly
|
||||
AuthSuccess AuthStatus = "AuthSuccess"
|
||||
@ -63,6 +64,7 @@ type authLogMessageData struct {
|
||||
Client,
|
||||
Host,
|
||||
Protocol,
|
||||
RequestID,
|
||||
RequestMethod,
|
||||
Timestamp,
|
||||
UserAgent,
|
||||
@ -75,6 +77,7 @@ type reqLogMessageData struct {
|
||||
Client,
|
||||
Host,
|
||||
Protocol,
|
||||
RequestID,
|
||||
RequestDuration,
|
||||
RequestMethod,
|
||||
RequestURI,
|
||||
@ -195,10 +198,12 @@ func (l *Logger) PrintAuthf(username string, req *http.Request, status AuthStatu
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
scope := middlewareapi.GetRequestScope(req)
|
||||
err := l.authTemplate.Execute(l.writer, authLogMessageData{
|
||||
Client: client,
|
||||
Host: requestutil.GetRequestHost(req),
|
||||
Protocol: req.Proto,
|
||||
RequestID: scope.RequestID,
|
||||
RequestMethod: req.Method,
|
||||
Timestamp: FormatTimestamp(now),
|
||||
UserAgent: fmt.Sprintf("%q", req.UserAgent()),
|
||||
@ -249,10 +254,12 @@ func (l *Logger) PrintReq(username, upstream string, req *http.Request, url url.
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
|
||||
scope := middlewareapi.GetRequestScope(req)
|
||||
err := l.reqTemplate.Execute(l.writer, reqLogMessageData{
|
||||
Client: client,
|
||||
Host: requestutil.GetRequestHost(req),
|
||||
Protocol: req.Proto,
|
||||
RequestID: scope.RequestID,
|
||||
RequestDuration: fmt.Sprintf("%0.3f", duration),
|
||||
RequestMethod: req.Method,
|
||||
RequestURI: fmt.Sprintf("%q", url.RequestURI()),
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
const RequestLoggingFormatWithoutTime = "{{.Client}} - {{.Username}} [TIMELESS] {{.Host}} {{.RequestMethod}} {{.Upstream}} {{.RequestURI}} {{.Protocol}} {{.UserAgent}} {{.StatusCode}} {{.ResponseSize}} {{.RequestDuration}}"
|
||||
const RequestLoggingFormatWithoutTime = "{{.Client}} - {{.RequestID}} - {{.Username}} [TIMELESS] {{.Host}} {{.RequestMethod}} {{.Upstream}} {{.RequestURI}} {{.Protocol}} {{.UserAgent}} {{.StatusCode}} {{.ResponseSize}} {{.RequestDuration}}"
|
||||
|
||||
var _ = Describe("Request logger suite", func() {
|
||||
type requestLoggerTableInput struct {
|
||||
@ -37,7 +37,10 @@ var _ = Describe("Request logger suite", func() {
|
||||
req.RemoteAddr = "127.0.0.1"
|
||||
req.Host = "test-server"
|
||||
|
||||
scope := &middlewareapi.RequestScope{Session: in.Session}
|
||||
scope := &middlewareapi.RequestScope{
|
||||
RequestID: "11111111-2222-4333-8444-555555555555",
|
||||
Session: in.Session,
|
||||
}
|
||||
req = middlewareapi.AddRequestScope(req, scope)
|
||||
|
||||
handler := NewRequestLogger()(testUpstreamHandler(in.Upstream))
|
||||
@ -47,7 +50,7 @@ var _ = Describe("Request logger suite", func() {
|
||||
},
|
||||
Entry("standard request", &requestLoggerTableInput{
|
||||
Format: RequestLoggingFormatWithoutTime,
|
||||
ExpectedLogMessage: "127.0.0.1 - standard.user [TIMELESS] test-server GET standard \"/foo/bar\" HTTP/1.1 \"\" 200 4 0.000\n",
|
||||
ExpectedLogMessage: "127.0.0.1 - 11111111-2222-4333-8444-555555555555 - standard.user [TIMELESS] test-server GET standard \"/foo/bar\" HTTP/1.1 \"\" 200 4 0.000\n",
|
||||
Path: "/foo/bar",
|
||||
ExcludePaths: []string{},
|
||||
Upstream: "standard",
|
||||
@ -55,7 +58,7 @@ var _ = Describe("Request logger suite", func() {
|
||||
}),
|
||||
Entry("with unrelated path excluded", &requestLoggerTableInput{
|
||||
Format: RequestLoggingFormatWithoutTime,
|
||||
ExpectedLogMessage: "127.0.0.1 - unrelated.exclusion [TIMELESS] test-server GET unrelated \"/foo/bar\" HTTP/1.1 \"\" 200 4 0.000\n",
|
||||
ExpectedLogMessage: "127.0.0.1 - 11111111-2222-4333-8444-555555555555 - unrelated.exclusion [TIMELESS] test-server GET unrelated \"/foo/bar\" HTTP/1.1 \"\" 200 4 0.000\n",
|
||||
Path: "/foo/bar",
|
||||
ExcludePaths: []string{"/ping"},
|
||||
Upstream: "unrelated",
|
||||
@ -69,7 +72,7 @@ var _ = Describe("Request logger suite", func() {
|
||||
}),
|
||||
Entry("ping path", &requestLoggerTableInput{
|
||||
Format: RequestLoggingFormatWithoutTime,
|
||||
ExpectedLogMessage: "127.0.0.1 - mr.ping [TIMELESS] test-server GET - \"/ping\" HTTP/1.1 \"\" 200 4 0.000\n",
|
||||
ExpectedLogMessage: "127.0.0.1 - 11111111-2222-4333-8444-555555555555 - mr.ping [TIMELESS] test-server GET - \"/ping\" HTTP/1.1 \"\" 200 4 0.000\n",
|
||||
Path: "/ping",
|
||||
ExcludePaths: []string{},
|
||||
Upstream: "",
|
||||
|
@ -3,18 +3,31 @@ package middleware
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/justinas/alice"
|
||||
middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
|
||||
)
|
||||
|
||||
func NewScope(reverseProxy bool) alice.Constructor {
|
||||
func NewScope(reverseProxy bool, idHeader string) alice.Constructor {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
scope := &middlewareapi.RequestScope{
|
||||
ReverseProxy: reverseProxy,
|
||||
RequestID: genRequestID(req, idHeader),
|
||||
}
|
||||
req = middlewareapi.AddRequestScope(req, scope)
|
||||
next.ServeHTTP(rw, req)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// genRequestID sets a request-wide ID for use in logging or error pages.
|
||||
// If a RequestID header is set, it uses that. Otherwise, it generates a random
|
||||
// UUID for the lifespan of the request.
|
||||
func genRequestID(req *http.Request, idHeader string) string {
|
||||
rid := req.Header.Get(idHeader)
|
||||
if rid != "" {
|
||||
return rid
|
||||
}
|
||||
return uuid.New().String()
|
||||
}
|
||||
|
@ -4,11 +4,19 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
||||
"github.com/google/uuid"
|
||||
middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
const (
|
||||
testRequestHeader = "X-Request-Id"
|
||||
testRequestID = "11111111-2222-4333-8444-555555555555"
|
||||
// mockRand io.Reader below counts bytes from 0-255 in order
|
||||
testRandomUUID = "00010203-0405-4607-8809-0a0b0c0d0e0f"
|
||||
)
|
||||
|
||||
var _ = Describe("Scope Suite", func() {
|
||||
Context("NewScope", func() {
|
||||
var request, nextRequest *http.Request
|
||||
@ -24,10 +32,11 @@ var _ = Describe("Scope Suite", func() {
|
||||
|
||||
Context("ReverseProxy is false", func() {
|
||||
BeforeEach(func() {
|
||||
handler := NewScope(false)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
nextRequest = r
|
||||
w.WriteHeader(200)
|
||||
}))
|
||||
handler := NewScope(false, testRequestHeader)(
|
||||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
nextRequest = r
|
||||
w.WriteHeader(200)
|
||||
}))
|
||||
handler.ServeHTTP(rw, request)
|
||||
})
|
||||
|
||||
@ -52,10 +61,11 @@ var _ = Describe("Scope Suite", func() {
|
||||
|
||||
Context("ReverseProxy is true", func() {
|
||||
BeforeEach(func() {
|
||||
handler := NewScope(true)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
nextRequest = r
|
||||
w.WriteHeader(200)
|
||||
}))
|
||||
handler := NewScope(true, testRequestHeader)(
|
||||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
nextRequest = r
|
||||
w.WriteHeader(200)
|
||||
}))
|
||||
handler.ServeHTTP(rw, request)
|
||||
})
|
||||
|
||||
@ -65,5 +75,53 @@ var _ = Describe("Scope Suite", func() {
|
||||
Expect(scope.ReverseProxy).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
Context("Request ID header is present", func() {
|
||||
BeforeEach(func() {
|
||||
request.Header.Add(testRequestHeader, testRequestID)
|
||||
handler := NewScope(false, testRequestHeader)(
|
||||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
nextRequest = r
|
||||
w.WriteHeader(200)
|
||||
}))
|
||||
handler.ServeHTTP(rw, request)
|
||||
})
|
||||
|
||||
It("sets the RequestID using the request", func() {
|
||||
scope := middlewareapi.GetRequestScope(nextRequest)
|
||||
Expect(scope.RequestID).To(Equal(testRequestID))
|
||||
})
|
||||
})
|
||||
|
||||
Context("Request ID header is missing", func() {
|
||||
BeforeEach(func() {
|
||||
uuid.SetRand(mockRand{})
|
||||
|
||||
handler := NewScope(true, testRequestHeader)(
|
||||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
nextRequest = r
|
||||
w.WriteHeader(200)
|
||||
}))
|
||||
handler.ServeHTTP(rw, request)
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
uuid.SetRand(nil)
|
||||
})
|
||||
|
||||
It("sets the RequestID using a random UUID", func() {
|
||||
scope := middlewareapi.GetRequestScope(nextRequest)
|
||||
Expect(scope.RequestID).To(Equal(testRandomUUID))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
type mockRand struct{}
|
||||
|
||||
func (mockRand) Read(p []byte) (int, error) {
|
||||
for i := range p {
|
||||
p[i] = byte(i % 256)
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
@ -401,6 +401,7 @@ var _ = Describe("Stored Session Suite", func() {
|
||||
}
|
||||
|
||||
req := httptest.NewRequest("", "/", nil)
|
||||
req = middlewareapi.AddRequestScope(req, &middlewareapi.RequestScope{})
|
||||
refreshed, err := s.refreshSessionWithProvider(nil, req, in.session)
|
||||
if in.expectedErr != nil {
|
||||
Expect(err).To(MatchError(in.expectedErr))
|
||||
|
@ -6,10 +6,16 @@ import (
|
||||
middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
|
||||
)
|
||||
|
||||
const (
|
||||
XForwardedProto = "X-Forwarded-Proto"
|
||||
XForwardedHost = "X-Forwarded-Host"
|
||||
XForwardedURI = "X-Forwarded-Uri"
|
||||
)
|
||||
|
||||
// GetRequestProto returns the request scheme or X-Forwarded-Proto if present
|
||||
// and the request is proxied.
|
||||
func GetRequestProto(req *http.Request) string {
|
||||
proto := req.Header.Get("X-Forwarded-Proto")
|
||||
proto := req.Header.Get(XForwardedProto)
|
||||
if !IsProxied(req) || proto == "" {
|
||||
proto = req.URL.Scheme
|
||||
}
|
||||
@ -19,7 +25,7 @@ func GetRequestProto(req *http.Request) string {
|
||||
// GetRequestHost returns the request host header or X-Forwarded-Host if
|
||||
// present and the request is proxied.
|
||||
func GetRequestHost(req *http.Request) string {
|
||||
host := req.Header.Get("X-Forwarded-Host")
|
||||
host := req.Header.Get(XForwardedHost)
|
||||
if !IsProxied(req) || host == "" {
|
||||
host = req.Host
|
||||
}
|
||||
@ -29,7 +35,7 @@ func GetRequestHost(req *http.Request) string {
|
||||
// GetRequestURI return the request URI or X-Forwarded-Uri if present and the
|
||||
// request is proxied.
|
||||
func GetRequestURI(req *http.Request) string {
|
||||
uri := req.Header.Get("X-Forwarded-Uri")
|
||||
uri := req.Header.Get(XForwardedURI)
|
||||
if !IsProxied(req) || uri == "" {
|
||||
// Use RequestURI to preserve ?query
|
||||
uri = req.URL.RequestURI()
|
||||
|
@ -390,7 +390,7 @@ var _ = Describe("HTTP Upstream Suite", func() {
|
||||
|
||||
handler := newHTTPUpstreamProxy(upstream, u, nil, nil)
|
||||
|
||||
proxyServer = httptest.NewServer(middleware.NewScope(false)(handler))
|
||||
proxyServer = httptest.NewServer(middleware.NewScope(false, "X-Request-Id")(handler))
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user