mirror of
https://github.com/ipfs/kubo.git
synced 2025-05-17 06:57:40 +08:00
core/corehttp: wrap gateway with headers, deprecate gateway /api/v0
This commit is contained in:
@ -850,6 +850,7 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e
|
||||
corehttp.GatewayOption("/ipfs", "/ipns"),
|
||||
corehttp.VersionOption(),
|
||||
corehttp.CheckVersionOption(),
|
||||
// TODO[api-on-gw]: remove for 0.28.0: https://github.com/ipfs/kubo/issues/10312
|
||||
corehttp.CommandsROOption(cmdctx),
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ipfs/boxo/gateway"
|
||||
cmds "github.com/ipfs/go-ipfs-cmds"
|
||||
cmdsHttp "github.com/ipfs/go-ipfs-cmds/http"
|
||||
version "github.com/ipfs/kubo"
|
||||
@ -149,6 +150,13 @@ func commandsOption(cctx oldcmds.Context, command *cmds.Command, allowGet bool)
|
||||
cmdHandler = withAuthSecrets(authorizations, cmdHandler)
|
||||
}
|
||||
|
||||
// TODO[api-on-gw]: remove for Kubo 0.28
|
||||
if command == corecommands.RootRO && allowGet {
|
||||
cmdHandler = gateway.NewHeaders(map[string][]string{
|
||||
"Link": {`<https://github.com/ipfs/kubo/issues/10312>; rel="deprecation"; type="text/html"`},
|
||||
}).Wrap(cmdHandler)
|
||||
}
|
||||
|
||||
cmdHandler = otelhttp.NewHandler(cmdHandler, "corehttp.cmdsHandler")
|
||||
mux.Handle(APIPath+"/", cmdHandler)
|
||||
return mux, nil
|
||||
|
@ -28,7 +28,7 @@ import (
|
||||
|
||||
func GatewayOption(paths ...string) ServeOption {
|
||||
return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
|
||||
config, err := getGatewayConfig(n)
|
||||
config, headers, err := getGatewayConfig(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -39,6 +39,7 @@ func GatewayOption(paths ...string) ServeOption {
|
||||
}
|
||||
|
||||
handler := gateway.NewHandler(config, backend)
|
||||
handler = gateway.NewHeaders(headers).ApplyCors().Wrap(handler)
|
||||
handler = otelhttp.NewHandler(handler, "Gateway")
|
||||
|
||||
for _, p := range paths {
|
||||
@ -51,7 +52,7 @@ func GatewayOption(paths ...string) ServeOption {
|
||||
|
||||
func HostnameOption() ServeOption {
|
||||
return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
|
||||
config, err := getGatewayConfig(n)
|
||||
config, headers, err := getGatewayConfig(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -65,6 +66,7 @@ func HostnameOption() ServeOption {
|
||||
|
||||
var handler http.Handler
|
||||
handler = gateway.NewHostnameHandler(config, backend, childMux)
|
||||
handler = gateway.NewHeaders(headers).ApplyCors().Wrap(handler)
|
||||
handler = otelhttp.NewHandler(handler, "HostnameGateway")
|
||||
|
||||
mux.Handle("/", handler)
|
||||
@ -240,22 +242,14 @@ var defaultKnownGateways = map[string]*gateway.PublicGateway{
|
||||
"localhost": subdomainGatewaySpec,
|
||||
}
|
||||
|
||||
func getGatewayConfig(n *core.IpfsNode) (gateway.Config, error) {
|
||||
func getGatewayConfig(n *core.IpfsNode) (gateway.Config, map[string][]string, error) {
|
||||
cfg, err := n.Repo.Config()
|
||||
if err != nil {
|
||||
return gateway.Config{}, err
|
||||
return gateway.Config{}, nil, err
|
||||
}
|
||||
|
||||
// Parse configuration headers and add the default Access Control Headers.
|
||||
headers := make(map[string][]string, len(cfg.Gateway.HTTPHeaders))
|
||||
for h, v := range cfg.Gateway.HTTPHeaders {
|
||||
headers[http.CanonicalHeaderKey(h)] = v
|
||||
}
|
||||
gateway.AddAccessControlHeaders(headers)
|
||||
|
||||
// Initialize gateway configuration, with empty PublicGateways, handled after.
|
||||
gwCfg := gateway.Config{
|
||||
Headers: headers,
|
||||
DeserializedResponses: cfg.Gateway.DeserializedResponses.WithDefault(config.DefaultDeserializedResponses),
|
||||
DisableHTMLErrors: cfg.Gateway.DisableHTMLErrors.WithDefault(config.DefaultDisableHTMLErrors),
|
||||
NoDNSLink: cfg.Gateway.NoDNSLink,
|
||||
@ -285,5 +279,5 @@ func getGatewayConfig(n *core.IpfsNode) (gateway.Config, error) {
|
||||
}
|
||||
}
|
||||
|
||||
return gwCfg, nil
|
||||
return gwCfg, cfg.Gateway.HTTPHeaders, nil
|
||||
}
|
||||
|
@ -206,7 +206,7 @@ func TestDeserializedResponsesInheritance(t *testing.T) {
|
||||
n, err := core.NewNode(context.Background(), &core.BuildCfg{Repo: r})
|
||||
assert.NoError(t, err)
|
||||
|
||||
gwCfg, err := getGatewayConfig(n)
|
||||
gwCfg, _, err := getGatewayConfig(n)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Contains(t, gwCfg.PublicGateways, "example.com")
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/ipfs/boxo/gateway"
|
||||
"github.com/ipfs/boxo/ipns"
|
||||
"github.com/ipfs/boxo/routing/http/server"
|
||||
"github.com/ipfs/boxo/routing/http/types"
|
||||
@ -18,7 +19,13 @@ import (
|
||||
|
||||
func RoutingOption() ServeOption {
|
||||
return func(n *core.IpfsNode, _ net.Listener, mux *http.ServeMux) (*http.ServeMux, error) {
|
||||
_, headers, err := getGatewayConfig(n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
handler := server.Handler(&contentRouter{n})
|
||||
handler = gateway.NewHeaders(headers).ApplyCors().Wrap(handler)
|
||||
mux.Handle("/routing/v1/", handler)
|
||||
return mux, nil
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
- [Overview](#overview)
|
||||
- [🔦 Highlights](#-highlights)
|
||||
- [Gateway: support for `/api/v0` is deprecated](#gateway-support-for-apiv0-is-deprecated)
|
||||
- [📝 Changelog](#-changelog)
|
||||
- [👨👩👧👦 Contributors](#-contributors)
|
||||
|
||||
@ -13,6 +14,12 @@
|
||||
|
||||
### 🔦 Highlights
|
||||
|
||||
#### Gateway: support for `/api/v0` is deprecated
|
||||
|
||||
Support for exposing the legacy subset of Kubo RPC via the Gateway port is deprecated and should not be used. It will be removed in the next version. You can read more in <https://github.com/ipfs/kubo/issues/10312>.
|
||||
|
||||
If you have a legacy software that relies on this behavior, and want to expose parts of `/api/v0` next to `/ipfs`, use reverse-proxy in front of Kubo to mount both Gateway and RPC on the same port. NOTE: exposing RPC to the internet comes with security risk: make sure to specify access control via [API.Authorizations](https://github.com/ipfs/kubo/blob/master/docs/config.md#apiauthorizations).
|
||||
|
||||
### 📝 Changelog
|
||||
|
||||
### 👨👩👧👦 Contributors
|
||||
|
@ -716,6 +716,8 @@ Toggle and configure experimental features of Kubo. Experimental features are li
|
||||
|
||||
Options for the HTTP gateway.
|
||||
|
||||
**NOTE:** support for `/api/v0` under the gateway path is now deprecated. It will be removed in future versions: https://github.com/ipfs/kubo/issues/10312.
|
||||
|
||||
### `Gateway.NoFetch`
|
||||
|
||||
When set to true, the gateway will only serve content already in the local repo
|
||||
@ -819,14 +821,14 @@ Example:
|
||||
"Gateway": {
|
||||
"PublicGateways": {
|
||||
"example.com": {
|
||||
"Paths": ["/ipfs", "/ipns"],
|
||||
"Paths": ["/ipfs"],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Above enables `http://example.com/ipfs/*` and `http://example.com/ipns/*` but not `http://example.com/api/*`
|
||||
Above enables `http://example.com/ipfs/*` but not `http://example.com/ipns/*`
|
||||
|
||||
Default: `[]`
|
||||
|
||||
@ -851,7 +853,6 @@ between content roots.
|
||||
}
|
||||
```
|
||||
- **Backward-compatible:** requests for content paths such as `http://{hostname}/ipfs/{cid}` produce redirect to `http://{cid}.ipfs.{hostname}`
|
||||
- **API:** if `/api` is on the `Paths` whitelist, `http://{hostname}/api/{cmd}` produces redirect to `http://api.{hostname}/api/{cmd}`
|
||||
|
||||
- `false` - enables [path gateway](https://docs.ipfs.tech/how-to/address-ipfs-on-web/#path-gateway) at `http://{hostname}/*`
|
||||
- Example:
|
||||
@ -860,7 +861,7 @@ between content roots.
|
||||
"PublicGateways": {
|
||||
"ipfs.io": {
|
||||
"UseSubdomains": false,
|
||||
"Paths": ["/ipfs", "/ipns", "/api"]
|
||||
"Paths": ["/ipfs", "/ipns"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -969,7 +970,7 @@ Below is a list of the most common public gateway setups.
|
||||
$ ipfs config --json Gateway.PublicGateways '{
|
||||
"ipfs.io": {
|
||||
"UseSubdomains": false,
|
||||
"Paths": ["/ipfs", "/ipns", "/api"]
|
||||
"Paths": ["/ipfs", "/ipns"]
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
@ -7,7 +7,7 @@ go 1.20
|
||||
replace github.com/ipfs/kubo => ./../../..
|
||||
|
||||
require (
|
||||
github.com/ipfs/boxo v0.17.1-0.20240112124340-bcb321c857c5
|
||||
github.com/ipfs/boxo v0.17.1-0.20240124092521-3d57bce7998c
|
||||
github.com/ipfs/kubo v0.0.0-00010101000000-000000000000
|
||||
github.com/libp2p/go-libp2p v0.32.2
|
||||
github.com/multiformats/go-multiaddr v0.12.1
|
||||
|
@ -260,8 +260,8 @@ github.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c h1:7Uy
|
||||
github.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c/go.mod h1:6EekK/jo+TynwSE/ZOiOJd4eEvRXoavEC3vquKtv4yI=
|
||||
github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
|
||||
github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
|
||||
github.com/ipfs/boxo v0.17.1-0.20240112124340-bcb321c857c5 h1:qGPYOK8flU2YzHGq9Cb2Yeo0jjOwompAOzxOv3VSGx8=
|
||||
github.com/ipfs/boxo v0.17.1-0.20240112124340-bcb321c857c5/go.mod h1:pIZgTWdm3k3pLF9Uq6MB8JEcW07UDwNJjlXW1HELW80=
|
||||
github.com/ipfs/boxo v0.17.1-0.20240124092521-3d57bce7998c h1:A18UHDQ4V2Ai6/YsrH7kfGjA1r5SrwjQR1Lqiq68YQU=
|
||||
github.com/ipfs/boxo v0.17.1-0.20240124092521-3d57bce7998c/go.mod h1:pIZgTWdm3k3pLF9Uq6MB8JEcW07UDwNJjlXW1HELW80=
|
||||
github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA=
|
||||
github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU=
|
||||
github.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk=
|
||||
|
2
go.mod
2
go.mod
@ -17,7 +17,7 @@ require (
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/ipfs-shipyard/nopfs v0.0.12
|
||||
github.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c
|
||||
github.com/ipfs/boxo v0.17.1-0.20240112124340-bcb321c857c5
|
||||
github.com/ipfs/boxo v0.17.1-0.20240124092521-3d57bce7998c
|
||||
github.com/ipfs/go-block-format v0.2.0
|
||||
github.com/ipfs/go-cid v0.4.1
|
||||
github.com/ipfs/go-cidutil v0.1.0
|
||||
|
4
go.sum
4
go.sum
@ -325,8 +325,8 @@ github.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c h1:7Uy
|
||||
github.com/ipfs-shipyard/nopfs/ipfs v0.13.2-0.20231027223058-cde3b5ba964c/go.mod h1:6EekK/jo+TynwSE/ZOiOJd4eEvRXoavEC3vquKtv4yI=
|
||||
github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
|
||||
github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
|
||||
github.com/ipfs/boxo v0.17.1-0.20240112124340-bcb321c857c5 h1:qGPYOK8flU2YzHGq9Cb2Yeo0jjOwompAOzxOv3VSGx8=
|
||||
github.com/ipfs/boxo v0.17.1-0.20240112124340-bcb321c857c5/go.mod h1:pIZgTWdm3k3pLF9Uq6MB8JEcW07UDwNJjlXW1HELW80=
|
||||
github.com/ipfs/boxo v0.17.1-0.20240124092521-3d57bce7998c h1:A18UHDQ4V2Ai6/YsrH7kfGjA1r5SrwjQR1Lqiq68YQU=
|
||||
github.com/ipfs/boxo v0.17.1-0.20240124092521-3d57bce7998c/go.mod h1:pIZgTWdm3k3pLF9Uq6MB8JEcW07UDwNJjlXW1HELW80=
|
||||
github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA=
|
||||
github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU=
|
||||
github.com/ipfs/go-bitswap v0.11.0 h1:j1WVvhDX1yhG32NTC9xfxnqycqYIlhzEzLXG/cU1HyQ=
|
||||
|
@ -103,7 +103,7 @@ require (
|
||||
github.com/hexops/gotextdiff v1.0.3 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/ipfs/bbloom v0.0.4 // indirect
|
||||
github.com/ipfs/boxo v0.17.1-0.20240112124340-bcb321c857c5 // indirect
|
||||
github.com/ipfs/boxo v0.17.1-0.20240124092521-3d57bce7998c // indirect
|
||||
github.com/ipfs/go-block-format v0.2.0 // indirect
|
||||
github.com/ipfs/go-cid v0.4.1 // indirect
|
||||
github.com/ipfs/go-datastore v0.6.0 // indirect
|
||||
|
@ -342,8 +342,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
|
||||
github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
|
||||
github.com/ipfs/boxo v0.17.1-0.20240112124340-bcb321c857c5 h1:qGPYOK8flU2YzHGq9Cb2Yeo0jjOwompAOzxOv3VSGx8=
|
||||
github.com/ipfs/boxo v0.17.1-0.20240112124340-bcb321c857c5/go.mod h1:pIZgTWdm3k3pLF9Uq6MB8JEcW07UDwNJjlXW1HELW80=
|
||||
github.com/ipfs/boxo v0.17.1-0.20240124092521-3d57bce7998c h1:A18UHDQ4V2Ai6/YsrH7kfGjA1r5SrwjQR1Lqiq68YQU=
|
||||
github.com/ipfs/boxo v0.17.1-0.20240124092521-3d57bce7998c/go.mod h1:pIZgTWdm3k3pLF9Uq6MB8JEcW07UDwNJjlXW1HELW80=
|
||||
github.com/ipfs/go-block-format v0.2.0 h1:ZqrkxBA2ICbDRbK8KJs/u0O3dlp6gmAuuXUJNiW1Ycs=
|
||||
github.com/ipfs/go-block-format v0.2.0/go.mod h1:+jpL11nFx5A/SPpsoBn6Bzkra/zaArfSmsknbPMYgzM=
|
||||
github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=
|
||||
|
@ -141,9 +141,11 @@ test_expect_success "Assert the default API.HTTPHeaders config is empty" '
|
||||
test_expect_success "Default CORS GET to {gw}/api/v0" '
|
||||
curl -svX GET -H "Origin: https://example.com" "http://127.0.0.1:$GWAY_PORT/api/v0/cat?arg=$thash" >/dev/null 2>curl_output
|
||||
'
|
||||
test_expect_success "Default CORS GET response from {gw}/api/v0 is 403 Forbidden and has no CORS headers" '
|
||||
# HTTP 403 is returned because Kubo has additional protections on top of regular CORS,
|
||||
# namely it only allows browser requests with localhost Origin header.
|
||||
test_expect_success "Default CORS GET response from {gw}/api/v0 is 403 Forbidden and has regular CORS headers" '
|
||||
test_should_contain "HTTP/1.1 403 Forbidden" curl_output &&
|
||||
test_should_not_contain "< Access-Control-" curl_output
|
||||
test_should_contain "< Access-Control-" curl_output
|
||||
'
|
||||
|
||||
# HTTP OPTIONS Request
|
||||
@ -151,8 +153,8 @@ test_expect_success "Default OPTIONS to {gw}/api/v0" '
|
||||
curl -svX OPTIONS -H "Origin: https://example.com" "http://127.0.0.1:$GWAY_PORT/api/v0/cat?arg=$thash" 2>curl_output
|
||||
'
|
||||
# OPTIONS Response from the API should NOT contain CORS headers
|
||||
test_expect_success "OPTIONS response from {gw}/api/v0 has no CORS header" '
|
||||
test_should_not_contain "< Access-Control-" curl_output
|
||||
test_expect_success "OPTIONS response from {gw}/api/v0 has CORS headers" '
|
||||
test_should_contain "< Access-Control-" curl_output
|
||||
'
|
||||
|
||||
test_kill_ipfs_daemon
|
||||
|
Reference in New Issue
Block a user