A tendermint upgrade 0.25.x -> 0.27.4 now forces me to set cors_allowed_origins instead of auto-generating the header from the request.
However, I am not able to set the value via command line (--rpc.cors_allowed_origins), as I do with laddr.
This is how I start Tendermint:
docker run --user="$UID" \
--name "$NAME" \
-p "${PORT}:26657" -v "${TMP_DIR}:/tendermint" \
-e "TM_TX_INDEX_INDEX_ALL_TAGS=true" \
"tendermint/tendermint:${VERSION}" node \
--proxy_app=kvstore \
--rpc.laddr=tcp://0.0.0.0:26657 \
--rpc.cors_allowed_origins='["*"]' \
--log_level=state:info,rpc:info,*:error \
> "$LOGFILE" &
Which leads to the following error output
$ ./scripts/tendermint/all_start.sh
Starting tendermint-27 (0.27.4) on port 11127 ...
Using temporary dir /var/folders/98/71tcbww552n4w9npxt0v0hmr0000gn/T//tendermint.ufN1PniE0
0.27.4: Pulling from tendermint/tendermint
Digest: sha256:6a6691c448554392ab05c0b5754489e02628d9539d3887458bdc4f69251697cd
Status: Image is up to date for tendermint/tendermint:0.27.4
I[28016-01-28|08:42:11.264] Generated private validator module=main path=/tendermint/config/priv_validator.json
I[28016-01-28|08:42:11.267] Generated node key module=main path=/tendermint/config/node_key.json
I[28016-01-28|08:42:11.274] Generated genesis file module=main path=/tendermint/config/genesis.json
Tendermint running and logging into /var/folders/98/71tcbww552n4w9npxt0v0hmr0000gn/T//tendermint.ufN1PniE0/tendermint.log
ERROR: unknown flag: --rpc.cors_allowed_origins
Yes, the command line flags are a small subset of the config flags, there is a clear mapping when they are present, but most are not exposed on the cli. The only option is to add this to the config:
Here are all command-line flags accepted by tendermint node:
https://github.com/tendermint/tendermint/blob/master/cmd/tendermint/commands/run_node.go#L14-L46
For comparison, here is the list of all rpc flags supported from the config:
https://github.com/tendermint/tendermint/blob/master/config/config.go#L285-L326
If you do not want to (or cannot) modify the config file, you can also use the little-documented environmental variables. Just as -e "TM_TX_INDEX_INDEX_ALL_TAGS=true" will set a config argument not exposed over the cli flags, you can also use a similar mapping to convert rpc.cors_allowed_origins to TM_RPC_CORS_ALLOWED_ORIGINS='*' (I think that is the proper format here, maybe '["*"]' as you wrote)
The environmental variables come from viper and are set up here and here.
Note:
// env variables with TM prefix (eg. TM_ROOT)
viper.SetEnvPrefix(prefix)
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
viper.AutomaticEnv()
This does the mapping of environmental variables to config file options. More info is available on the viper home page.
There is little clarity there on the parsing of Slices (arrays): https://github.com/spf13/viper#getting-values-from-viper is the only mention of Slices. But from previous experience, cobra and viper use csv parser to parse the command line variables, and likely the environmental variables as well (but not toml). So the argument passed to TM_RPC_CORS_ALLOWED_ORIGINS will be interpreted as a comma-delimited csv file.
Note to @zramsay, maybe this belongs in the docs...
Want to expand and add something? You can ask me for a review if you want, as I was the original author of this use of viper, implementing it originally for cosmos-sdk v0.7.0
Thanks! Unfortunately I did not have luck with any of those
-e "TM_RPC_CORS_ALLOWED_ORIGINS='*'"
-e "TM_RPC_CORS_ALLOWED_ORIGINS='\"*\"'"
-e "TM_RPC_CORS_ALLOWED_ORIGINS='[\"*\"]'"
Not sure we want to expose every config parameter as a CLI flag. Can't you change the config.toml file?
Can't you change the config.toml file?
No idea how to do that. Right now I have no config file I am aware of and would need to add infrastructure for that. I can probably find out.
But when we want to have the simple use cases simple, I think we should use a different CORS origin default: *. CORS is browser technology only and in this API there are no session headers et cetera that need to be protected.
Right now I have no config file
check ~/.tendermint/config/ or $TMHOME/config directories.
But when we want to have the simple use cases simple, I think we should use a different CORS origin default: *. CORS is browser technology only and in this API there are no session headers et cetera that need to be protected.
We should allow access from the same domain Tendermint is running on by default! Similar to Kafka https://github.com/apache/kafka/commit/eb823281a52f3b27c3a889e7412bc07b3024e688. Want to send a PR?
from the same domain Tendermint is running on by default!
Hmm, what is the domain of a locally running Tendermint? Then I can access from a website on http://localhost but not http://127.0.0.1?
Is there anything that CORS can protect in case of the Tendermint RPC interface?
Want to send a PR?
Sorry, don't speak golang :(
Sorry, don't speak golang :(
no problem, shouldn't take much time anyway
Then I can access from a website on http://localhost but not http://127.0.0.1?
not sure. if it can follow redirects, then you should be able to access.
Is there anything that CORS can protect in case of the Tendermint RPC interface?
I am no expert unfortunately. That's why I've searched how others are doing it.
Unpopular opinion here: CORS in the current setup is not really useful. We should rip it out of tendermint and leave it to the web security layer above (nginx or load-balancers). OR we have to implement TLS too. The only people who could maybe benefit from the current setup are developers and developer setups usually don't need CORS. Also, it cannot be nicely implemented in production.
Because of the above two properties, when implementing a web frontend for tendermint, there has to be a TLS layer implemented somewhere (nginx, ELB, etc) so the website can connect to tendermint over https. That TLS layer already supports CORS and it's usually well understood how to set it up.
I discourage the team from implementing TLS into tendermint - for now. TLS is a complex technology and implementing a half-baked solution does more harm to security than completely leaving it to mature products. GaiaD is suffering from exactly this problem. Self-signed certificates (since you need to have _something_ at start) are a false sense of security and a pain in the butt when trying to implement any load-balancing in front of the service. Not to mention, that client websites will still have problems with it, until the certificate is imported as trusted.
Obviously, there might be better solution here that I didn't discuss in length, this note is just to raise a few key points. If we keep going in this direction, it would be nice to have an option to completely disable CORS/TLS the same way we have the option to enable/disable routing restrictions based on internal IP addresses. (I had a similar argument back then, to not implement IP routing rules in Tendermint and the compromise was that it can be enabled/disabled.)
Thanks for raising your concerns @greg-szabo 馃憤 Definitely some food for thought here. We'll probably need to think more before making any further decisions on this.
Note: CORS can be turned on/off using the config (it's off by default now).
Original issue: https://github.com/tendermint/tendermint/issues/2582
Note: CORS can be turned on/off using the config (it's off by default now).
Note: in this case _CORS turned off_ means no modern webbrowser can access any Tendermint API call. There more interesting flag would be: allow all / restrict access. Both are active CORS configurations.
@webmaster128 Is that true even if you run the web site and the tendermint API on the same machine? I was under the assumption that CORS is only necessary for cross-domain calls, not calls on the same machine.
Also, just to clarify: all web-browsers will be able to access all Tendermint API (http://whatever.com:26657/status will still work.) Web frameworks like Javascript might have problems calling into the Tendermint API without CORS, right?
@greg-szabo I just verified (I'm not an expert here): a website at http://localhost:9876 is not allowd to access Tendermint at http://localhost:11127/. This makes sense when you check the definition of origin in CORS: _An origin is defined as a combination of URI scheme, host name, and port number._
Some tests what you can access from a browser:
<iframe src="http://localhost:11127/status"></iframe> works<script>fetch("http://localhost:11127/status")</script> does not work<script>fetch("http://localhost:11127/status", {mode: "cors"})</script> does not work <script>fetch("http://localhost:11127/status", {mode: "no-cors"})</script> performs a request but the result is not available in JavaScript<script>fetch("http://localhost:11127/status", {mode: "same-origin"})</script> is blocked because it is not the same originSo the only thing you can do from a browser in terms of API access is having a manual loock at the JSON document.
Thanks! Unfortunately I did not have luck with any of those
-e "TM_RPC_CORS_ALLOWED_ORIGINS=''"
-e "TM_RPC_CORS_ALLOWED_ORIGINS='\"\"'"
-e "TM_RPC_CORS_ALLOWED_ORIGINS='[\"*\"]'"
Note, the reason it is like this, is we use the generic tendermint docker image, but want to configure it run time, rather than add a new subimage and docker build. Seems like a reasonable use-case.
I think there are two issues:
A tested solution to point 1 would close this issue. I think the discussion around 2 is valuable to have as a separate issue to finalize how cors should work by the 1.0 release
After some digging and adding custom instrumentation into tendermint config parsing logic, I determined this is impossible for slice arguments, only for "normal" arguments. This is likely due to viper handling...
TM_RPC_CORS_ALLOWED_ORIGINS=bing,baz,bam TM_RPC_MAX_OPEN_CONNECTIONS=777 tendermint node --proxy_app=kvstore
will produce the following items from viper.AllSettings():
"rpc": {
"cors_allowed_headers": [
"Origin",
"Accept",
"Content-Type",
"X-Requested-With",
"X-Server-Time"
],
"cors_allowed_methods": [
"HEAD",
"GET",
"POST"
],
"cors_allowed_origins": "bing,baz,bam",
"grpc_laddr": "",
"grpc_max_open_connections": 900,
"laddr": "tcp://0.0.0.0:26657",
"max_open_connections": "777",
(note that data is set, but cors_allowed_origins is a string, not a slice of strings...)
If I print the RPCConfig after viper.Unmarshal(conf) in root.go:ParseConfig, I get:
&config.RPCConfig{
RootDir:"",
ListenAddress:"tcp://0.0.0.0:26657",
CORSAllowedOrigins:[]string{"98", "105", "110", "103", "44", "98", "97", "122", "44", "98", "97", "109"},
CORSAllowedMethods:[]string{"HEAD", "GET", "POST"},
CORSAllowedHeaders:[]string{"Origin", "Accept", "Content-Type", "X-Requested-With", "X-Server-Time"},
GRPCListenAddress:"",
GRPCMaxOpenConnections:900,
Unsafe:false,
MaxOpenConnections:777
}
You can see how MaxOpenConnections made it in, but the CORSAllowedOrigin ended up with a slice of all the character values
There is a work-around for this, but it involves updating spf13/viper to a newer version than the v1.0.0 that is currently locked to.
https://github.com/spf13/viper/pull/521 provides an optional flag to viper.Unmarshal
If you upgrade to a more recent version (eg. v1.3.1), you could do the following in cmd/tendermint/commands/root.go:
func ParseConfig() (*cfg.Config, error) {
conf := cfg.DefaultConfig()
codec := viper.DecodeHook(mapstructure.StringToSliceHookFunc(","))
err := viper.Unmarshal(conf, codec)
This would allow parsing out slices of strings from environmental variables.
Unfortunately, @webmaster128 this means for the time being, you are going to somehow have to get your hands on the config file and run it through sed.
sed -ie 's/cors_allowed_origins.*$/cors_allowed_origins = ["*"]/' config.toml
@melekes I just tried to do the above update and validated that simply updating viper dependency in Gopkg.toml and running dep ensure will make the string slice parsing work.
[[constraint]]
name = "github.com/spf13/viper"
version = "^1.3.1"
Not really such a bad idea, since the currenly pinned v1.0.0 is a year and a half old....
Most helpful comment
Unpopular opinion here: CORS in the current setup is not really useful. We should rip it out of tendermint and leave it to the web security layer above (nginx or load-balancers). OR we have to implement TLS too. The only people who could maybe benefit from the current setup are developers and developer setups usually don't need CORS. Also, it cannot be nicely implemented in production.
Because of the above two properties, when implementing a web frontend for tendermint, there has to be a TLS layer implemented somewhere (nginx, ELB, etc) so the website can connect to tendermint over https. That TLS layer already supports CORS and it's usually well understood how to set it up.
I discourage the team from implementing TLS into tendermint - for now. TLS is a complex technology and implementing a half-baked solution does more harm to security than completely leaving it to mature products. GaiaD is suffering from exactly this problem. Self-signed certificates (since you need to have _something_ at start) are a false sense of security and a pain in the butt when trying to implement any load-balancing in front of the service. Not to mention, that client websites will still have problems with it, until the certificate is imported as trusted.
Obviously, there might be better solution here that I didn't discuss in length, this note is just to raise a few key points. If we keep going in this direction, it would be nice to have an option to completely disable CORS/TLS the same way we have the option to enable/disable routing restrictions based on internal IP addresses. (I had a similar argument back then, to not implement IP routing rules in Tendermint and the compromise was that it can be enabled/disabled.)