From 0a19a00a4e7a33cc09e02357e817e287c105a1cf Mon Sep 17 00:00:00 2001 From: Ilias Rinis Date: Wed, 2 Sep 2026 11:56:19 +0200 Subject: [PATCH] Add agentic SDLC context files --- AGENTS.md | 47 +++++ ARCHITECTURE.md | 54 ++++++ CONTRIBUTING.md | 221 ++++++++++++++++++++++ README.md | 473 ++++++++---------------------------------------- 4 files changed, 402 insertions(+), 393 deletions(-) create mode 100644 AGENTS.md create mode 100644 ARCHITECTURE.md create mode 100644 CONTRIBUTING.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..f55ad7d0b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,47 @@ +# AGENTS.md + +`oauth-proxy` is an OpenShift-focused reverse proxy that authenticates requests with the cluster OAuth server and can authorize them with Kubernetes APIs before forwarding them upstream. + +## Commands + +```sh +make build +make test-unit +go test ./providers/openshift/... +go test . -run TestName +make test-e2e # requires a configured OpenShift cluster; serial, 3h timeout +``` + +After dependency changes, run `go mod tidy`, `go mod vendor`, and the unit tests. The Makefile uses vendored `openshift/build-machinery-go`; inspect the included makefiles before assuming a target exists. + +## Repository Map + +```text +main.go flags, config loading, startup and shutdown +options.go defaults and configuration validation +oauthproxy.go OAuth handlers, sessions, routing and reverse proxy +http.go HTTP/HTTPS listeners and dynamic certificates +providers/openshift/ OpenShift OAuth, authentication and authorization +providers/ provider contract and session representation +cookie/ signed and encrypted cookies +api/, util/ HTTP, certificate and file helpers +contrib/ deployment and configuration examples +test/e2e/ cluster-dependent end-to-end tests +``` + +See [ARCHITECTURE.md](ARCHITECTURE.md) for request flows and [CONTRIBUTING.md](CONTRIBUTING.md) for the development workflow. + +## Engineering Rules + +- Add co-located `*_test.go` coverage; prefer table-driven tests for related cases. +- Run `gofmt` on changed Go files and `make test-unit` before submitting. +- Keep flags in `main.go`, `Options` fields/tags/defaults in `options.go`, validation, examples, and tests aligned. +- Preserve `RequestURI`/`URL.Opaque` handling when changing proxy directors; it prevents encoded slash decoding. See `TestEncodedSlashes`. +- Strip or overwrite client-supplied authentication and forwarding headers before adding trusted identity headers. Include adversarial normalization tests. +- Propagate an available `context.Context` and keep shutdown/watchers tied to it. +- Use `library-go/pkg/crypto` secure TLS defaults. Do not weaken verification to make a deployment work. +- Never log or commit secrets, tokens, private keys, kubeconfigs, or real credentials. +- Never edit `vendor/` by hand; use Go module commands and commit all resulting changes. + +Ask maintainers before changing public flags/defaults, endpoints, cookie formats, identity headers, OAuth scopes, or authentication/authorization cache semantics. Changes under `providers/openshift/` should consider interactive OAuth, delegated credentials, and SubjectAccessReview authorization, including malformed and deny paths. + diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 000000000..abde481b7 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,54 @@ +# Architecture + +## Overview + +`oauth-proxy` sits in front of HTTP(S) applications or static files. It supports interactive OpenShift OAuth, validates the resulting session, optionally authorizes the user through Kubernetes APIs, and proxies requests with configured identity headers. It is commonly a sidecar beside an application listening only inside the pod. + +```text +browser/API client + -> HTTP/HTTPS server (http.go) + -> OAuthProxy.ServeHTTP (oauthproxy.go) + -> OAuth endpoints -> OpenShift OAuth server + -> session/authz -> Kubernetes/OpenShift API + -> delegated authn/authz for bearer tokens or client certificates + -> HTTP(S), WebSocket, or file upstream +``` + +## Startup and Configuration + +`main.go` defines flags, reads an optional TOML file, applies supported environment variables, and resolves them into `Options`. `options.go` validates configuration and parses upstream and redirect URLs. The OpenShift provider can discover cluster OAuth endpoints and, with `--openshift-service-account`, derive the client ID from the namespace/account and read the mounted token as its client secret. + +Startup constructs `OAuthProxy`, optionally loads htpasswd data and a pprof listener, then serves until SIGTERM or interrupt cancels the root context. HTTPS serving certificates are watched and reloaded. + +## Request Flow + +With the default `--proxy-prefix=/oauth`, proxy-owned paths are: + +| Path | Purpose | +| --- | --- | +| `/robots.txt` | Disallow crawlers | +| `/oauth/healthz` | Liveness | +| `/oauth/sign_in`, `/oauth/sign_out` | Login UI and session removal | +| `/oauth/start`, `/oauth/callback` | Start and complete OAuth | +| `/oauth/auth` | Accepted/unauthorized response for `auth_request` | + +For an ordinary request, the proxy applies protected-path and bypass rules; verifies and refreshes the session cookie; falls back to Basic Auth or delegated authentication when no session remains; validates identity rules and SubjectAccessReviews; normalizes untrusted header variants; adds configured trusted headers; and dispatches to the selected upstream. An unauthenticated browser enters OAuth, while failed delegated API authentication returns an error. + +## OpenShift Provider + +The provider contract is in `providers/providers.go`; the binary currently selects only OpenShift. `providers/openshift/` implements OAuth discovery/redemption/user lookup, session refresh, global and host-specific access reviews, TokenAccessReview and client-certificate authentication, SubjectAccessReview authorization, caches, and cluster CA handling. + +`--openshift-sar` checks an interactively logged-in identity. `--openshift-delegate-urls` authenticates credentials supplied on the request and authorizes the longest matching path. Delegated bearer tokens are forwarded only when `--pass-user-bearer-token` is also enabled, extending the security boundary to the upstream. + +## Sessions, Upstreams, and Headers + +Session state lives in `providers/session_state.go`. Cookies are signed; contents are encrypted when token storage or refresh requires it. A separate CSRF cookie binds callback to login. Serialization is a compatibility boundary requiring tampering, expiry, and round-trip tests. + +Each `--upstream` is registered by URL path. HTTP(S) uses Go's reverse proxy with HTTP/2 transport, WebSocket upgrades use the WebSocket proxy, and `file://` serves local files. Encoded paths are deliberately preserved. + +Client-supplied variants of identity headers are normalized and removed. Configuration controls Basic Auth, `X-Forwarded-*`, access-token, and `X-Auth-Request-*` headers. Optional `GAP-Signature` HMAC protects selected request data for an upstream that verifies it. + +## TLS + +HTTP accepts TCP or Unix-socket addresses. HTTPS uses OpenShift secure TLS defaults and dynamically reloads its certificate/key. Upstream and OpenShift CA bundles have separate trust pools. Insecure certificate verification is a legacy escape hatch and should not be used in production. + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..65b137a3e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,221 @@ +# Contributing to the OpenShift Control Plane Components/Repositories + +This document serves as a guide for contributing to the OpenShift components/repositories +that the OpenShift Control Plane group is responsible for maintaining. + +This document is explicitly for contributions to individual component repositories and not for high-level +feature proposals within OpenShift. + +Feature proposals should follow the OpenShift Enhancement Proposal process outlined in https://github.com/openshift/enhancements/blob/master/dev-guide/feature-zero-to-hero.md#openshift-feature-development-zero-to-hero-guide. +If you are looking for a review on an OpenShift Enhancement Proposal that involves changes to components +maintained by the control plane group, please request a review in the [`#forum-ocp-apiserver`](https://redhat.enterprise.slack.com/archives/CB48XQ4KZ) Slack channel. Requests for review may be redirected to more appropriate and/or more focused channels for discussion. + +This document contains the following sections: + +- [Code conventions](#code-conventions) - A collection of guidelines, style suggestions, and tips for writing code. +- [Testing guidelines](#testing-guidelines) - Guidelines and expectations for testing of contributions. +- [Pull Request process/guidelines](#pull-request-process-and-guidelines) - Guidelines and expectations of pull requests containing contributions. +- [Review expectations](#review-expectations) - Guidelines and expectations for requesting reviews and interacting with reviewers. + +## Code Conventions + +We largely follow the [Kubernetes Code Conventions](https://github.com/kubernetes/community/blob/main/contributors/guide/coding-conventions.md#code-conventions). + +Review both the Kubernetes Code Conventions and the ones specified here. +There will be some overlap. If any conventions are at odds with one another, prefer the conventions explicitly documented here. + +### Bash + +- Follow the [shell styleguide](https://google.github.io/styleguide/shellguide.html). +- Use [`shellcheck`](https://github.com/koalaman/shellcheck) to identify common mistakes or caveats. +- Ensure that all scripts run consistently across Linux and MacOS. + +### Golang (Go) + +- Review [Effective Go](https://go.dev/doc/effective_go). +- Review common [Go Code Review Comments](https://go.dev/wiki/CodeReviewComments). +- Review and avoid [Go Landmines](https://gist.github.com/lavalamp/4bd23295a9f32706a48f) +- Comment your code following the [Go comment conventions](https://go.dev/doc/comment). + - Comments should be meaningful and add context and/or explain choices that cannot be expressed through clear code. + - All exported types, functions, and methods must have descriptive comments. + - All unexported types, functions, and methods should have descriptive comments. +- When adding command-line flags, use dashes/hyphens (`-`) and not underscores (`_`). +- Naming + - Please consider package name when selecting an interface name, and avoid redundancy. For example, `storage.Interface` is better than `storage.StorageInterface`. + - Do not use uppercase characters, underscores, or dashes in package names. + - Please consider parent directory name when choosing a package name. For example, `pkg/controllers/autoscaler/foo.go` should say `package autoscaler` not `package autoscalercontroller`. + - Unless there's a good reason, the package foo line should match the name of the directory in which the .go file exists. + - Importers can use a different name if they need to disambiguate. + - Locks should be called `lock` and should never be embedded (always `lock sync.Mutex`). When multiple locks are present, give each lock a distinct name following Go conventions: `stateLock`, `mapLock` etc. +- Context propagation + - When a function accepts or has access to a `context.Context`, pass it through to downstream calls that accept one. Never discard a context or substitute `context.Background()`/`context.TODO()` when a context is already available. +- Error handling + - Wrap errors with meaningful context before returning or logging them. +- When logging, follow the [Kubernetes Logging Conventions](https://github.com/kubernetes/community/blob/main/contributors/devel/sig-instrumentation/logging.md). +- When patching OpenShift-maintained forks of "upstream" repositories, patches should be as small as reasonably possible and should minimize touch points with code that is likely to change and impact the rebasing process. +- Dependencies must be vendored. When making changes to dependencies, ensure you've run `go mod tidy` and `go mod vendor`. + +### General + +Regardless of the programming language, make sure to take the following into consideration: + +- Keep readability / maintainability in mind when writing code. + - Clever code and abstractions are often harder to reason about after the fact. Keep clever code and abstractions to the minimum necessary to accomplish the end-goal. +- Do not reinvent the wheel. Where possible, use existing standard library or vendored library functionality. If you are adding a net-new dependency, stop and think if you _really_ need to add the new dependency to achieve your goals. +- When writing tests, focus on testing the functional behaviors your code exercises. Avoid writing tests that are testing that the standard library works as expected or is trivial. Do not write tests just for line coverage. + +### Directory and File Conventions + +- Avoid package sprawl. Find an appropriate subdirectory for new packages. + - Libraries with no appropriate home belong in new package subdirectories of `pkg/util`. +- Avoid general utility packages. Packages called "util" are suspect. Instead, derive a name that describes your desired function. For example, the utility functions dealing with waiting for operations are in the `wait` package and include functionality like `Poll`. The full name is `wait.Poll`. +- All filenames should be lowercase. +- Go source files and directories use underscores, not dashes. + - Package directories should generally avoid using separators as much as possible. When package names are multiple words, they usually should be in nested subdirectories. + +## Testing Guidelines + +These are high-level testing guidelines. Where individual component repositories may have +additional testing guidelines to follow when making contributions. + +- All changes must include unit test additions/changes. + - Exceptions are at reviewer/approver discretion. +- Table-driven unit tests are preferred for testing multiple scenarios/inputs. For an example, see https://github.com/openshift/cluster-authentication-operator/blob/a493799952e9b6838021ccc7d15d3d37d7ad3508/pkg/controllers/externaloidc/externaloidc_controller_test.go#L108 . +- Unit tests must pass on all platforms (at the very least, Linux + MacOS). +- Significant features should come with integration and/or end-to-end (e2e) tests where appropriate. + - End-to-end tests _may_ be scoped as a separate work item when the end-to-end tests for the component must be added to the openshift/origin repository instead of the component repository. Adding e2e tests to the component repository is preferred where possible. It is up to reviewer/approver discretion whether a contribution can be merged without end-to-end tests being implemented. +- Do not expect an asynchronous thing to happen immediately. Do not wait for one second and expect a pod to be running. Wait and retry instead. + +If necessary, manual integration testing can be done by creating a cluster using the [`Cluster Bot` Slack App](https://redhat.enterprise.slack.com/archives/D03KX7M1CRJ). +Once you have a cluster created, you can follow some of the instructions in https://github.com/openshift/enhancements/blob/master/dev-guide/operators.md for guidance on how to +build component images and modify cluster-operators to deploy those images. + +Most component repos have existing tooling to run unit tests. Check for Makefiles and shell scripts that might run the unit tests. If none exist, you should be able to use standard testing tooling like `go test ./...` to run all tests for the project. https://pkg.go.dev/cmd/go/internal/test is a good reference for how the `go test` command works. + +## Pull Request Process and Guidelines + +This section assumes that you have a functional understanding of `git` and how to create a pull request on GitHub. + +If you do not, start with [GitHub's "Getting Started" guide](https://docs.github.com/en/get-started/start-your-journey). + +### Prerequisites + +Before you commit any changes or create any pull requests, you must adhere to OpenShift contribution policies. +Currently, that means enabling commit signature verification. + +See https://docs.google.com/document/d/1184EPSGunUkcSQYUK8T4a6iyawwi6f2zxdbB2jtG9nQ/edit?usp=sharing for more details on +how to adhere to the commit signature verification policy of OpenShift. + +### Creating a Pull Request + +When creating a pull request, include the following: + +- A brief, but descriptive, title. + - All pull requests _should_ link to a Jira ticket associated with the work. There is automation that performs this linking when prefixing the title with the Jira ticket identifier like: `CNTRLPLANE-XXXX: my pull request title`. For pull requests that have no Jira ticket associated with it, you can prefix it with `NO-JIRA:` to signal that there is not a Jira ticket associated with it. +- A useful description of the changes being made and why they are important. Include links to supporting documents and any additional context that reviewers may need. + +### CI / CD + +For CI/CD, OpenShift uses Prow to run various checks. This can include unit tests, e2e tests, linters, etc. + +The jobs configured for each repository are in https://github.com/openshift/release/tree/main/ci-operator/config/openshift . If you find yourself needing to add additional jobs, review the documentation at https://docs.ci.openshift.org/how-tos/contributing-openshift-release/ . + +There are often a mixture of required and optional checks as well as merge criteria that must be met before a pull request can merge. +When any of these checks fail, the GitHub Prow bot will leave a comment on the PR with links to the run of that check that failed. + +As the PR author, it is your responsibility to evaluate the failed checks and determine if there are any changes necessary to pass the checks. +If you suspect that the check failure was a flake, you can trigger retests by commenting `/retest` (or `/retest-required` for retesting only the required checks) on the PR. + +### Verifying your changes / Creating an OpenShift cluster from a PR + +As part of merging a PR, there is a requirement to verify that the changes you've made are working as expected using the `/verified` comment command. + +While there are a lot of scenarios where the existing CI/CD checks may be sufficient to verify your changes are working (and can be denoted by commenting `/verified by ci`), +there may be scenarios where manual verification is required. + +You can use the `Cluster Bot` Slack App to create a cluster from a PR by sending it a message in the format of `launch ${OCP_VERSION},${PR_LINK} ${PLATFORM},${VARIANT}`. +As an example, `launch 4.23,https://github.com/openshift/oauth-proxy/pull/373 aws,techpreview` would launch an OpenShift 4.23 cluster with the changes made in openshift/oauth-proxy#373 running on AWS with the TechPreviewNoUpgrade feature-set enabled. +For more information on what `Cluster Bot` can do, you can send it a message saying `help` and it will respond with additional documentation on how it can be used. + +Once you've verified your changes work as expected, you can mark the PR as verified by commenting `/verified by @{your_github_handle}` on the PR. + +### Additional Resources + +For more information regarding more general OpenShift pull request processes, the following resources are helpful: + +- https://docs.ci.openshift.org/architecture/jira +- https://docs.ci.openshift.org/ + https://steps.ci.openshift.org/ + +## Review Expectations + +### Requesting a review + +If you are not a member of the OpenShift control plane team and you need a review on a PR, post it in the [#forum-ocp-apiserver](https://redhat.enterprise.slack.com/archives/CB48XQ4KZ) Slack channel or +reach out to folks outlined in the OWNERS file directly. + +If you are a member of the OpenShift control plane team, reviews should come from your feature team. In the event your feature team does not have someone that can approve +a PR, post it in the [#control-plane](https://redhat.enterprise.slack.com/archives/CC3CZCQHM) Slack channel. + +OpenShift uses AI code review tools as part of the code review process. +Before requesting a review, address all feedback from the code review agent(s). +It is up to your discretion as the contributor how you would like to address that feedback. +Responding with an explanation as to why you are not going to take action on a comment made +by the agent is an acceptable way to "address" its feedback. + +### Interacting with reviewers + +When interacting with reviewers/approvers: + +- Be professional. +- Be respectful of differing opinions, viewpoints, and experiences. +- Gracefully give and receive constructive feedback. +- Focus on what is best for the product/organization, not just us as individuals. + +A special note on the usage of AI - to respect the time of those that are reviewing your contribution, please do not use AI to respond to review comments. + +# Specific Guidelines for `oauth-proxy` + +## Pre-Submit Checks + +Before pushing your changes, run: + +```sh +make build +make test-unit +``` + +The unit target runs the repository's non-e2e Go packages with the race detector. Focused tests can be run with standard Go tooling, for example: + +```sh +go test ./providers/openshift/... +go test . -run TestName +``` + +## Dependency Management + +This repository vendors all Go dependencies. After adding, removing, or updating a dependency: + +```sh +go mod tidy +go mod vendor +make test-unit +``` + +Commit `go.mod`, `go.sum`, and the resulting `vendor/` changes together. Do not edit files under `vendor/` directly. + +## Configuration Changes + +When adding or changing configuration, keep flag registration in `main.go` or the OpenShift provider's `Bind` methods aligned with the `Options` fields, TOML keys, supported environment tags, defaults, validation, examples, tests, and user documentation. + +Changes to public flags, defaults, endpoints, cookie/session formats, forwarded identity headers, OAuth scopes, or authentication and authorization caches require particular compatibility and security review. + +## E2E Test Suite + +Cluster-dependent tests are under `test/e2e/`: + +| Suite | Makefile target | Timeout | Notes | +| --- | --- | --- | --- | +| `test/e2e/` | `make test-e2e` | 3h | Serial (`-p 1`); requires a configured OpenShift cluster | + +The target disables test caching and runs the package serially because tests share cluster resources. Do not parallelize it without accounting for those resources. diff --git a/README.md b/README.md index db9224b47..d1c18584a 100644 --- a/README.md +++ b/README.md @@ -1,446 +1,133 @@ -OpenShift oauth-proxy -===================== +# OpenShift oauth-proxy -A reverse proxy and static file server that provides authentication and authorization to an OpenShift OAuth -server or Kubernetes master supporting the 1.6+ remote authorization endpoints to validate access to content. -It is intended for use within OpenShift clusters to make it easy to run both end-user and infrastructure -services that don't provide their own authentication. +`oauth-proxy` is a reverse proxy and static file server that authenticates requests with OpenShift OAuth before forwarding them upstream. It can also delegate bearer-token or client-certificate authentication and authorization to the Kubernetes API. -Features: +It is usually deployed as a sidecar beside an application that listens only inside the pod. A Service and Route expose the proxy while the application remains unreachable directly. -* Performs zero-configuration OAuth when run as a pod in OpenShift -* Able to perform simple authorization checks against the OpenShift and Kubernetes RBAC policy engine to grant access -* May also be configured to check bearer tokens or Kubernetes client certificates and verify access -* On OpenShift 3.6+ clusters, supports zero-configuration end-to-end TLS via the out of the box router +Capabilities include automatic OAuth client configuration from an OpenShift service account, SubjectAccessReview authorization, delegated API authentication, path-based HTTP(S)/WebSocket/file upstreams, signed sessions with CSRF protection, and dynamic serving-certificate reload. -This is a fork of the https://github.com/bitly/oauth2_proxy project with other providers removed (for now). It's -focused on providing the simplest possible secure proxy on OpenShift +![Sign-in page](front.png) -![Sign In Page](https://raw.githubusercontent.com/openshift/oauth-proxy/master/front.png) +## Quick Start on OpenShift -## Using this proxy with OpenShift - -This proxy is best used as a sidecar container in a Kubernetes pod, protecting another server that listens -only on localhost. On an OpenShift cluster, it can use the service account token as an OAuth client secret -to identify the current user and perform access control checks. For example: - - $ ./oauth-proxy --upstream=http://localhost:8080 --cookie-secret=SECRET \ - --openshift-service-account=default --https-address= - -will start the proxy against localhost:8080, encrypt the login cookie with SECRET, use the default -service account in the current namespace, and only listen on http. - -A full sidecar example is in [contrib/sidecar.yaml](contrib/sidecar.yaml) which also demonstrates using -OpenShift TLS service serving certificates (giving you an automatic in-cluster cert) with an external route. -Run against a 3.6+ cluster with: - - $ oc create -f https://raw.githubusercontent.com/openshift/oauth-proxy/master/contrib/sidecar.yaml - -The OpenShift provider defaults to allowing any user that can log into the OpenShift cluster - the following -sections cover more on restricting access. - -### Limiting access to users - -While you can use the `--email-domain` and `--authenticated-emails-file` to match users directly, -the proxy works best when you delegate authorization to the OpenShift master by specifying what permissions -you expect the user to have. This allows you to leverage OpenShift RBAC and groups to map users to -permissions centrally. - -#### Require specific permissions to login via OAuth with `--openshift-sar=JSON` - -SAR stands for "Subject Access Review", which is a request sent to the OpenShift or Kubernetes server -to check the access for a particular user. Expects a single subject access review JSON object, or -a JSON array, all of which must be satisfied for a user to be able to access the backend server. - -Pros: - -* Easiest way to protect an entire website or API with an OAuth flow -* Requires no additional permissions to be granted for the proxy service account - -Cons: - -* Not well suited for service-to-service access -* All-or-nothing protection for the upstream server - -Example: - - # Allows access if the user can view the service 'proxy' in namespace 'app-dev' - --openshift-sar='{"namespace":"app-dev","resource":"services","resourceName":"proxy","verb":"get"}' - -A user who visits the proxy will be redirected to an OAuth login with OpenShift, and must grant -access to the proxy to view their user info and request permissions for them. Once they have granted -that right to the proxy, it will check whether the user has the required permissions. If they do -not, they'll be given a permission denied error. If they are, they'll be logged in via a cookie. - -Run `oc explain subjectaccessreview` to see the schema for a review, including other fields. -Specifying multiple rules via a JSON array (`[{...}, {...}]`) will require all permissions to -be granted. - -##### Require specific permissions per host with `--openshift-sar-by-host=JSON` - -This is similar to the `--openshift-sar` option but instead of the rules applying to all hosts, you -can set up specific rules that are checked for a particular upstream host. Using a JSON object the -keys are hostnames and the value is a JSON array of SAR rules. - -Both `--openshift-sar` and `--openshift-sar-by-host` can be used together which will require all -of the rules from the former as well as any rules that match the host to be satisified for a user -to be able to access the backed server. - -Example: - - # Allows access to foo.example.com if the user can view the service 'proxy' in namespace 'app-dev' - --openshift-sar-by-host='{"foo.example.com":{"namespace":"app-dev","resource":"services","resourceName":"proxy","verb":"get"}}' - -#### Delegate authentication and authorization to OpenShift for infrastructure - -OpenShift leverages bearer tokens for end users and for service accounts. When running -infrastructure services, it may be easier to delegate all authentication and authoration to -the master. The `--openshift-delegate-urls=JSON` flag enables delegation, asking the master -to validate any incoming requests with an `Authorization: Bearer` header or client certificate -to be forwarded to the master for verification. If the user authenticates, they are then -checked against one of the entries in the provided map - -The value of the flag is a JSON map of path prefixes to `v1beta1.ResourceAttributes`, and the -longest path prefix is checked. If no path matches the request, authentication and authorization -are skipped. - -Pros: - -* Allow other OpenShift service accounts or infrastructure components to authorize to specific APIs - -Cons: - -* Not suited for web browser use -* Should not be used by untrusted components (can steal tokens) - -Example: - - # Allows access if the provided bearer token has view permission on a custom resource - --openshift-delegate-urls='{"/":{"group":"custom.group","resource":"myproxy","verb":"get"}}' - - # Grant access only to paths under /api - --openshift-delegate-urls='{"/api":{"group":"custom.group","resource":"myproxy","verb":"get"}}' - -WARNING: Because users are sending their own credentials to the proxy, it's important to use this -setting only when the proxy is under control of the cluster administrators. Otherwise, end users -may unwittingly provide their credentials to untrusted components that can then act as them. - -When configured for delegation, Oauth Proxy will not set the `X-Forwarded-Access-Token` header on -the upstream request. If you wish to forward the bearer token received from the client, you will -have to use the `--pass-user-bearer-token` option in addition to `--openshift-delegate-urls`. - -WARNING: With `--pass-user-bearer-token` the client's bearer token will be passed upstream. This -could pose a security risk if the token is misused or leaked from the upstream service. Bear in -mind that the tokens received from client could be long term and hard to revoke. - -### Other configuration flags - -#### `--openshift-service-account=NAME` - -Will attempt to read the `--client-id` and `--client-secret` from the service account information -injected by OpenShift. Uses the value of `/var/run/secrets/kubernetes.io/serviceaccount/namespace` -to build the correct `--client-id`, and the contents of -`/var/run/secrets/kubernetes.io/serviceaccount/token` as the `--client-secret`. - -#### `--openshift-ca` - -One or more paths to CA certificates that should be used when connecting to the OpenShift master. -If none are provided, the proxy will default to using `/var/run/secrets/kubernetes.io/serviceaccount/ca.crt`. - - -### Discovering the OAuth configuration of an OpenShift cluster - -OpenShift supports the `/.well-known/oauth-authorization-server` endpoint, which returns a JSON document -describing the authorize and token URLs, as well as the default scopes. If you are running outside -of OpenShift you can specify these flags directly using the existing flags for these URLs. - - -### Configuring the proxy's service account in OpenShift - -In order for service accounts to be used as OAuth clients, they must have the [proper OAuth annotations set](https://docs.openshift.org/latest/architecture/additional_concepts/authentication.html#service-accounts-as-oauth-clients). -to point to a valid external URL. In most cases, this can be a route exposing the service fronting your -proxy. We recommend using a `Reencrypt` type route and [service serving certs](https://docs.openshift.org/latest/dev_guide/secrets.html#service-serving-certificate-secrets) to maximize end to end -security. See [contrib/sidecar.yaml](contrib/sidecar.yaml) for an example of these used in concert. - -By default, the redirect URI of a service account set up as an OAuth client must point to an HTTPS endpoint which -is a common configuration error. +This starts the proxy in front of localhost, uses the mounted `default` service account as its OAuth client, and listens over HTTP: +```sh +./oauth-proxy \ + --upstream=http://localhost:8080 \ + --cookie-secret="$(openssl rand -base64 32)" \ + --openshift-service-account=default \ + --https-address= +``` -## Developing +See [contrib/sidecar.yaml](contrib/sidecar.yaml) for a sidecar deployment with a Route and service-serving certificate. Replace its placeholder cookie secret; in production, inject it from a Kubernetes Secret instead of pod arguments or source control. The service account must have an OAuth redirect annotation matching the Route, as shown in the example. -To build, ensure you are running Go 1.7+ and clone the repo: +## Authorization Modes -``` -$ go get -u github.com/openshift/oauth-proxy -$ cd $GOPATH/src/github.com/openshift/oauth-proxy -``` +### Interactive OAuth and RBAC -To build, run: +By default, any authenticated user is accepted. Email rules can restrict identities, but OpenShift RBAC is usually the better policy source. `--openshift-sar` accepts one rule or an array; every rule must allow the user: -``` -$ go test . +```sh +--openshift-sar='{"namespace":"app-dev","resource":"services","resourceName":"proxy","verb":"get"}' ``` -The docker images for this repository are built by [the OpenShift release process](https://github.com/openshift/release/blob/master/projects/oauth-proxy/pipeline.yaml) and are available at +Host-specific rules supplied with `--openshift-sar-by-host` are combined with global rules: +```sh +--openshift-sar-by-host='{"foo.example.com":{"namespace":"app-dev","resource":"services","resourceName":"proxy","verb":"get"}}' ``` -$ docker pull registry.svc.ci.openshift.org/ci/oauth-proxy:v1 -``` - -## End-to-end testing -To run the end-to-end test suite against a build of the current commit on an OpenShift cluster, use test/e2e.sh. You may need to change the DOCKER_REPO, KUBECONFIG, and TEST_NAMESPACE variables to accommodate your cluster. -Each test sets up an oauth-proxy deployment and steps through the OAuth process, ensuring that the backend site can be reached (or not, depending on the test). The deployment is deleted before running the next test. -DEBUG_TEST=testname can be used to skip the cleanup step for a specific test and halt the suite to allow for further debugging on the cluster. +### Delegated API Access -$ test/e2e.sh +`--openshift-delegate-urls` validates credentials on an incoming request and authorizes the longest matching path prefix. This is intended for infrastructure APIs, not browser login: -## Architecture - -![OAuth2 Proxy Architecture](https://cloud.githubusercontent.com/assets/45028/8027702/bd040b7a-0d6a-11e5-85b9-f8d953d04f39.png) +```sh +--openshift-delegate-urls='{"/api":{"group":"example.io","resource":"widgets","verb":"get"}}' +``` +Only use delegation when cluster administrators control the proxy: clients send credentials to it. Delegation does not forward bearer tokens upstream unless `--pass-user-bearer-token` is also set; that flag extends trust to the upstream application. ## Configuration -`oauth-proxy` can be configured via [config file](#config-file), [command line options](#command-line-options) or [environment variables](#environment-variables). - -To generate a strong cookie secret use `python -c 'import os,base64; print base64.b64encode(os.urandom(16))'` - -### Email Authentication - -To authorize by email domain use `--email-domain=yourcompany.com`. To authorize individual email addresses use `--authenticated-emails-file=/path/to/file` with one email per line. To authorize all email addresses use `--email-domain=*`. +Configuration comes from an optional TOML file, supported environment variables, and command-line flags. Start with [contrib/oauth-proxy.cfg.example](contrib/oauth-proxy.cfg.example) and use `--config=/path/to/oauth-proxy.cfg`. -### Config File +Run the binary's help for the authoritative flags and current defaults: -An example [oauth-proxy.cfg](contrib/oauth-proxy.cfg.example) config file is in the contrib directory. It can be used by specifying `-config=/etc/oauth-proxy.cfg` - -### Command Line Options - -``` -Usage of oauth-proxy: - -approval-prompt string: OAuth approval_prompt (default "force") - -authenticated-emails-file string: authenticate against emails via file (one per line) - -basic-auth-password string: the password to set when passing the HTTP Basic Auth header - -bypass-auth-except-for value: provide authentication ONLY for request paths under proxy-prefix and those that match the given regex (may be given multiple times). Cannot be set with -skip-auth-regex - -bypass-auth-for value: alias for -skip-auth-regex - -client-id string: the OAuth Client ID: ie: "123456.apps.googleusercontent.com" - -client-secret string: the OAuth Client Secret - -config string: path to config file - -cookie-domain string: an optional cookie domain to force cookies to (ie: .yourcompany.com)* - -cookie-expire duration: expire timeframe for cookie (default 168h0m0s) - -cookie-httponly: set HttpOnly cookie flag (default true) - -cookie-name string: the name of the cookie that the oauth_proxy creates (default "_oauth2_proxy") - -cookie-refresh duration: refresh the cookie after this duration; 0 to disable - -cookie-samesite string | set SameSite cookie attribute (ie: `"lax"`, `"strict"`, `"none"`, or `""`) - -cookie-secret string: the seed string for secure cookies (optionally base64 encoded) - -cookie-secret-file string: same as "-cookie-secret" but read it from a file - -cookie-secure: set secure (HTTPS) cookie flag (default true) - -custom-templates-dir string: path to custom html templates - -display-htpasswd-form: display username / password login form if an htpasswd file is provided (default true) - -email-domain value: authenticate emails with the specified domain (may be given multiple times). Use * to authenticate any email - -footer string: custom footer string. Use "-" to disable default footer. - -htpasswd-file string: additionally authenticate against a htpasswd file. Entries must be created with "htpasswd -s" for SHA encryption - -http-address string: [http://]: or unix:// to listen on for HTTP clients (default "127.0.0.1:4180") - -https-address string: : to listen on for HTTPS clients (default ":443") - -login-url string: Authentication endpoint - -pass-access-token: pass OAuth access_token to upstream via X-Forwarded-Access-Token header - -pass-user-bearer-token: pass OAuth access token received from the client to upstream via X-Forwarded-Access-Token header - -pass-basic-auth: pass HTTP Basic Auth, X-Forwarded-User and X-Forwarded-Email information to upstream (default true) - -pass-host-header: pass the request Host Header to upstream (default true) - -pass-user-headers: pass X-Forwarded-User and X-Forwarded-Email information to upstream (default true) - -profile-url string: Profile access endpoint - -provider string: OAuth provider (default "google") - -proxy-prefix string: the url root path that this proxy should be nested under (e.g. //sign_in) (default "/oauth") - -proxy-websockets: enables WebSocket proxying (default true) - -redeem-url string: Token redemption endpoint - -redirect-url string: the OAuth Redirect URL. ie: "https://internalapp.yourcompany.com/oauth2/callback" - -request-logging: Log requests to stdout (default false) - -scope string: OAuth scope specification - -set-xauthrequest: set X-Auth-Request-User and X-Auth-Request-Email response headers (useful in Nginx auth_request mode) - -signature-key string: GAP-Signature request signature key (algorithm:secretkey) - -skip-auth-preflight: will skip authentication for OPTIONS requests - -skip-auth-regex value: bypass authentication for requests path's that match (may be given multiple times). Cannot be set with -bypass-auth-except-for - -skip-provider-button: will skip sign-in-page to directly reach the next step: oauth/start - -ssl-insecure-skip-verify: skip validation of certificates presented when using HTTPS - -tls-cert string: path to certificate file - -tls-key string: path to private key file - -upstream value: the http url(s) of the upstream endpoint or file:// paths for static files. Routing is based on the path - -upstream-timeout duration: maximum amount of time the server will wait for a response from the upstream (default 30s) - -validate-url string: Access token validation endpoint - -version: print version string +```sh +./oauth-proxy --help ``` -See below for provider specific options +| Option | Purpose | +| --- | --- | +| `--upstream` | HTTP(S) URL or `file://` tree; repeat for path routing | +| `--openshift-service-account` | Derive OAuth client credentials in-cluster | +| `--openshift-sar` | Require access-review rules | +| `--openshift-delegate-urls` | Delegate authn/authz on matching API paths | +| `--cookie-secret` / `--cookie-secret-file` | Always sign cookies; encrypt session token fields only when `--pass-access-token` or `--cookie-refresh` is enabled | +| `--tls-cert`, `--tls-key` | Serve HTTPS with dynamically reloaded files | +| `--upstream-ca`, `--openshift-ca` | Configure upstream and API trust roots | +| `--skip-auth-regex` | Bypass authentication on matching paths | +| `--proxy-prefix` | Set proxy-owned path prefix (default `/oauth`) | -### Upstream Configuration +Supported environment variables are the `env` tags on `Options` in `options.go`. They currently cover client credentials, cookie settings, and the request signature key. Prefer file-based secrets where available. -`oauth-proxy` supports having multiple upstreams, and has the option to pass requests on to HTTP(S) servers or serve static files from the file system. HTTP and HTTPS upstreams are configured by providing a URL such as `http://127.0.0.1:8080/` for the upstream parameter, that will forward all authenticated requests to be forwarded to the upstream server. If you instead provide `http://127.0.0.1:8080/some/path/` then it will only be requests that start with `/some/path/` which are forwarded to the upstream. +### Upstreams -Static file paths are configured as a file:// URL. `file:///var/www/static/` will serve the files from that directory at `http://[oauth-proxy url]/var/www/static/`, which may not be what you want. You can provide the path to where the files should be available by adding a fragment to the configured URL. The value of the fragment will then be used to specify which path the files are available at. `file:///var/www/static/#/static/` will ie. make `/var/www/static/` available at `http://[oauth-proxy url]/static/`. +Repeat `--upstream` to route different prefixes: -Multiple upstreams can either be configured by supplying a comma separated list to the `-upstream` parameter, supplying the parameter multiple times or provinding a list in the [config file](#config-file). When multiple upstreams are used routing to them will be based on the path they are set up with. +```sh +--upstream=http://127.0.0.1:8080/ \ +--upstream=http://127.0.0.1:9090/api/ +``` -### Environment variables +HTTP(S) upstreams support ordinary and WebSocket traffic. For files, a URL fragment selects the public path: `file:///var/www/assets/#/static/` serves that directory below `/static/`. -The following environment variables can be used in place of the corresponding command-line arguments: +### TLS -- `OAUTH2_PROXY_CLIENT_ID` -- `OAUTH2_PROXY_CLIENT_SECRET` -- `OAUTH2_PROXY_COOKIE_NAME` -- `OAUTH2_PROXY_COOKIE_SAMESITE` -- `OAUTH2_PROXY_COOKIE_SECRET` -- `OAUTH2_PROXY_COOKIE_DOMAIN` -- `OAUTH2_PROXY_COOKIE_EXPIRE` -- `OAUTH2_PROXY_COOKIE_REFRESH` -- `OAUTH2_PROXY_SIGNATURE_KEY` +For direct TLS termination, set `--tls-cert` and `--tls-key`; replacements are reloaded dynamically. To terminate TLS elsewhere, set `--https-address=` and explicitly configure the needed HTTP bind address. Keep `--cookie-secure=true` when browsers use public HTTPS. -## SSL Configuration +Do not use `--ssl-insecure-skip-verify` in production. Configure the appropriate CA option instead. -There are two recommended configurations. +## Proxy Endpoints -1) Configure SSL Terminiation with OAuth2 Proxy by providing a `--tls-cert=/path/to/cert.pem` and `--tls-key=/path/to/cert.key`. +With the default `--proxy-prefix=/oauth`: -The command line to run `oauth-proxy` in this configuration would look like this: +| Endpoint | Behavior | +| --- | --- | +| `/robots.txt` | Disallows crawlers | +| `/oauth/healthz` | Returns `200 OK` | +| `/oauth/sign_in`, `/oauth/sign_out` | Login UI and session removal | +| `/oauth/start`, `/oauth/callback` | Starts and completes OAuth | +| `/oauth/auth` | Returns accepted/unauthorized without proxying | -```bash -./oauth-proxy \ - --email-domain="yourcompany.com" \ - --upstream=http://127.0.0.1:8080/ \ - --tls-cert=/path/to/cert.pem \ - --tls-key=/path/to/cert.key \ - --cookie-secret=... \ - --cookie-secure=true \ - --provider=... \ - --client-id=... \ - --client-secret=... -``` +All other matched requests go upstream after applicable access checks. +## Forwarded Identity and Signing -2) Configure SSL Termination with [Nginx](http://nginx.org/) (example config below), Amazon ELB, Google Cloud Platform Load Balancing, or .... +Configuration can enable Basic Auth, `X-Forwarded-User`, `X-Forwarded-Email`, `X-Forwarded-Access-Token`, and `X-Auth-Request-*`. Treat the upstream as part of the security boundary when sensitive identity or token headers are enabled. -Because `oauth-proxy` listens on `127.0.0.1:4180` by default, to listen on all interfaces (needed when using an -external load balancer like Amazon ELB or Google Platform Load Balancing) use `--http-address="0.0.0.0:4180"` or -`--http-address="http://:4180"`. +With `--signature-key=algorithm:secret`, proxied requests receive a `GAP-Signature` HMAC over selected request data. The upstream must verify it; signing does not encrypt traffic. -Nginx will listen on port `443` and handle SSL connections while proxying to `oauth-proxy` on port `4180`. -`oauth-proxy` will then authenticate requests for an upstream application. The external endpoint for this example -would be `https://internal.yourcompany.com/`. +## Build and Test -An example Nginx config follows. Note the use of `Strict-Transport-Security` header to pin requests to SSL -via [HSTS](http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security): +Use the Go version declared by `go.mod` (currently 1.25). Dependencies are vendored. -``` -server { - listen 443 default ssl; - server_name internal.yourcompany.com; - ssl_certificate /path/to/cert.pem; - ssl_certificate_key /path/to/cert.key; - add_header Strict-Transport-Security max-age=2592000; - - location / { - proxy_pass http://127.0.0.1:4180; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Scheme $scheme; - proxy_connect_timeout 1; - proxy_send_timeout 30; - proxy_read_timeout 30; - } -} +```sh +make build +make test-unit ``` -The command line to run `oauth-proxy` in this configuration would look like this: +Cluster-dependent end-to-end tests require a configured OpenShift cluster and run serially with a three-hour timeout: -```bash -./oauth-proxy \ - --email-domain="yourcompany.com" \ - --upstream=http://127.0.0.1:8080/ \ - --cookie-secret=... \ - --cookie-secure=true \ - --provider=... \ - --client-id=... \ - --client-secret=... +```sh +make test-e2e ``` -## Endpoint Documentation - -oauth-proxy responds directly to the following endpoints. All other endpoints will be proxied upstream when authenticated. The `/oauth` prefix can be changed with the `--proxy-prefix` config variable. - -* /robots.txt - returns a 200 OK response that disallows all User-agents from all paths; see [robotstxt.org](http://www.robotstxt.org/) for more info -* /oauth/healthz - returns an 200 OK response -* /oauth/sign_in - the login page, which also doubles as a sign out page (it clears cookies) -* /oauth/start - a URL that will redirect to start the OAuth cycle -* /oauth/callback - the URL used at the end of the OAuth cycle. The oauth app will be configured with this as the callback url. -* /oauth/auth - only returns a 202 Accepted response or a 401 Unauthorized response; for use with the [Nginx `auth_request` directive](#nginx-auth-request) +See [CONTRIBUTING.md](CONTRIBUTING.md) for development details and [ARCHITECTURE.md](ARCHITECTURE.md) for internal design. -## Request signatures +Container images are produced by the OpenShift release process. Use the image reference for the OpenShift release you target, rather than the obsolete CI-only `registry.svc.ci.openshift.org/ci/oauth-proxy:v1` reference. -If `signature-key` is defined, proxied requests will be signed with the -`GAP-Signature` header, which is a [Hash-based Message Authentication Code -(HMAC)](https://en.wikipedia.org/wiki/Hash-based_message_authentication_code) -of selected request information and the request body [see `SIGNATURE_HEADERS` -in `oauthproxy.go`](./oauthproxy.go). +## History -`signature_key` must be of the form `algorithm:secretkey`, (ie: `signature_key = "sha1:secret0"`) - -For more information about HMAC request signature validation, read the -following: - -* [Amazon Web Services: Signing and Authenticating REST - Requests](https://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html) -* [rc3.org: Using HMAC to authenticate Web service - requests](http://rc3.org/2011/12/02/using-hmac-to-authenticate-web-service-requests/) - -## Logging Format - -oauth-proxy logs requests to stdout in a format similar to Apache Combined Log. - -``` - - [19/Mar/2015:17:20:19 -0400] GET "/path/" HTTP/1.1 "" -``` - -## Configuring for use with the Nginx `auth_request` directive - -The [Nginx `auth_request` directive](http://nginx.org/en/docs/http/ngx_http_auth_request_module.html) allows Nginx to authenticate requests via the oauth-proxy's `/auth` endpoint, which only returns a 202 Accepted response or a 401 Unauthorized response without proxying the request through. For example: - -```nginx -server { - listen 443 ssl spdy; - server_name ...; - include ssl/ssl.conf; - - location = /oauth2/auth { - internal; - proxy_pass http://127.0.0.1:4180; - } - - location /oauth2/ { - proxy_pass http://127.0.0.1:4180; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Scheme $scheme; - proxy_set_header X-Auth-Request-Redirect $request_uri; - } - - location /upstream/ { - auth_request /oauth2/auth; - error_page 401 = /oauth2/sign_in; - - # pass information via X-User and X-Email headers to backend, - # requires running with --set-xauthrequest flag - auth_request_set $user $upstream_http_x_auth_request_user; - auth_request_set $email $upstream_http_x_auth_request_email; - proxy_set_header X-User $user; - proxy_set_header X-Email $email; - - proxy_pass http://backend/; - } - - location / { - auth_request /oauth2/auth; - error_page 401 = https://example.com/oauth2/sign_in; - - root /path/to/the/site; - } -} -``` +This project originated as an OpenShift-focused fork of Bitly's `oauth2_proxy`. The current binary supports the OpenShift provider.