Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Authenticator: Bearer_token w. "query_parameter" selector consumes request body #1105

Open
6 tasks done
marbergq opened this issue May 24, 2023 · 0 comments
Open
6 tasks done
Labels
bug Something is not working.

Comments

@marbergq
Copy link
Contributor

marbergq commented May 24, 2023

Preflight checklist

Describe the bug

GIven this rule

      authenticators:
      - handler: bearer_token
        config:
          check_session_url: http://check-session-host/...
          token_from:
            query_parameter: token

The upstream service will never see the body when the request is of Content-type: application/x-www-form-urlencoded.

This is because return r.FormValue(*tokenLocation.QueryParameter) (in helper/bearer.go) will consume the body

Reproducing the bug

config.yaml:

  api:
    port: 6061
  proxy:
    port: 6060

access_rules:
  repositories:
    - file://./rules.1.json

authenticators:
  noop:
    enabled: true
  bearer_token:
    enabled: true
    config:
      check_session_url: http://localhost:6662/session

authorizers:
  allow:
    enabled: true
  deny:
    enabled: true

mutators:
  noop:
    enabled: true

rules.1.yaml

[
  {
    "id": "test",
    "upstream": {
      "url": "http://127.0.0.1:6662"
    },
    "match": {
      "url": "http://127.0.0.1:6060/test",
      "methods": ["POST"]
    },
    "authenticators": [
      {
        "handler": "bearer_token",
        "config": {
          "check_session_url": "http://127.0.0.1:6662/session",
          "preserve_path": true,
          "preserve_query": false,
          "force_method": "GET",
          "token_from": {
            "query_parameter": "token"
          }
        }
      }
    ],
    "authorizer": {
      "handler": "allow"
    },
    "mutators": [
      {
        "handler": "noop"
      }
    ]
  }
]

run.sh

#!/bin/bash

set -euo pipefail

waitport() {
  i=0
  while ! nc -z localhost "$1" ; do
    sleep 1
    if [ $i -gt 10 ]; then
      cat ./config.yaml
      cat ./oathkeeper.log
      exit 1
    fi
    i=$((i+1))
  done
}

cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"


run_oathkekeper() {
  killall oathkeeper || true

  export OATHKEEPER_PROXY=http://127.0.0.1:6060
  export OATHKEEPER_API=http://127.0.0.1:6061

  go build -o . ../..
  LOG_LEVEL=debug ./oathkeeper --config ./config.yaml serve > ./oathkeeper.log 2>&1 &

  waitport 6060
  waitport 6061
}

run_api(){
  killall okapi || true

  PORT=6662 go run ./okapi > ./api.log 2>&1 &

  waitport 6662
}

SUCCESS_TEST=()
FAILED_TEST=()

run_test() {
  label=$1
  shift 1

  result="0"
  "$@" || result="1"

  if [[ "$result" -eq "0" ]]; then
    SUCCESS_TEST+=("$label")
  else
    FAILED_TEST+=("$label")
  fi
}

function finish {
  echo ::group::Config
  cat ./config.yaml
  cat ./rules.1.json
  echo ::endgroup::
  echo ::group::Log
  cat ./oathkeeper.log
  echo ::endgroup::
}
trap finish EXIT

run_oathkekeper
run_api

curl -X POST -f http://127.0.0.1:6662/test?token=token -F fk=fv -H "Content-Type: application/x-www-form-urlencoded" -i

kill %1 || true

trap - EXIT

okapi/main.go

// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"

	"github.com/julienschmidt/httprouter"
)

func main() {
	router := httprouter.New()

	router.POST("/test", test)
	router.GET("/session", session)

	port := os.Getenv("PORT")
	if port == "" {
		port = "6662"
	}

	server := http.Server{
		Addr:    fmt.Sprintf(":%s", port),
		Handler: router,
	}

	if err := server.ListenAndServe(); err != nil {
		panic(err)
	}
}

func test(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
	}
	fmt.Printf("body was: %s", string(b))
	_, _ = w.Write(b)
}

func session(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	w.WriteHeader(http.StatusOK)
}

$> ./run.sh

time=2023-05-24T14:07:31+02:00 level=warning msg=Access request denied because roundtrip failed audience=application error=map[message:net/http: HTTP/1.x transport connection broken: http: ContentLength=140 with Body length 0 stack_trace: 
....

Relevant log output

time=2023-05-24T14:07:31+02:00 level=warning msg=Access request denied because roundtrip failed audience=application error=map[message:net/http: HTTP/1.x transport connection broken: http: ContentLength=140 with Body length 0 stack_trace:

Relevant configuration

[
  {
    "id": "test",
    "upstream": {
      "url": "http://127.0.0.1:6662"
    },
    "match": {
      "url": "http://127.0.0.1:6060/test",
      "methods": ["POST"]
    },
    "authenticators": [
      {
        "handler": "bearer_token",
        "config": {
          "check_session_url": "http://127.0.0.1:6662/session",
          "preserve_path": true,
          "preserve_query": false,
          "token_from": {
            "query_parameter": "token"
          }
        }
      }
    ],
    "authorizer": {
      "handler": "allow"
    },
    "mutators": [
      {
        "handler": "noop"
      }
    ]
  }
]

Version

0.40.3

On which operating system are you observing this issue?

None

In which environment are you deploying?

Kubernetes

Additional Context

No response

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

No branches or pull requests

1 participant