hashicorp / consul

Consul is a distributed, highly available, and data center aware solution to connect and configure applications across dynamic, distributed infrastructure.
https://www.consul.io
Other
28.31k stars 4.42k forks source link

Web UI .js files code ignores -ui-content-path flag #10374

Open ptecza opened 3 years ago

ptecza commented 3 years ago

Overview of the Issue

I noticed that Web UI of Consul v1.9.3 generates .js files which ignore -ui-content-path flag. I checked the latest Consul code and it seems that it has a hardcoded GET /v1/... HTTP requests as well. I have to use sub_filter rules of my Nginx reverse proxy to fix the invalid requests.

Reproduction Steps

Steps to reproduce this issue:

  1. Create a cluster with 3 nodes using -ui and -ui-content-path /foo/ui/ flags for them
  2. Configure Nginx reverse proxy for handling the cluster, e.g.:
    
    upstream foo_consul_backend {
        server consul1.foo.example.com:8500;
        server consul2.foo.example.com:8500;
        server consul3.foo.example.com:8500;
    }

server { listen 443; server_name consul.example.com;

    ssl on;
    ssl_certificate     /etc/letsencrypt/live/consul.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/consul.example.com/privkey.pem;

    access_log /var/log/nginx/consul-access-ssl.log;
    error_log  /var/log/nginx/consul-error-ssl.log;

    location / {
            root /var/www/html/consul/;
            index index.html;
    }

    location /foo/ui/ {
            proxy_set_header Accept-Encoding "";
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass http://foo_consul_backend/foo/ui/;
    }
    location /foo/v1/ {
            proxy_pass http://foo_consul_backend/v1/;
    }
3. Go to https://consul.example.com/foo/ui/ using your favourite web browser
4. See an empty page

### Consul info for Server

<details>
  <summary>Server info</summary>

agent: check_monitors = 1 check_ttls = 0 checks = 1 services = 0 build: prerelease = revision = f55da930 version = 1.9.3 consul: acl = disabled bootstrap = false known_datacenters = 1 leader = false leader_addr = 10.10.10.15:8300 server = true raft: applied_index = 774 commit_index = 774 fsm_pending = 0 last_contact = 84.006927ms last_log_index = 774 last_log_term = 2 last_snapshot_index = 0 last_snapshot_term = 0 latest_configuration = [{Suffrage:Voter ID:24796312-70d4-4cb4-b7b6-558ca82edecb Address:10.10.10.13:8300} {Suffrage:Voter ID:7c50b85e-0389-4408-986d-5a0162f52f2e Address:10.10.10.14:8300} {Suffrage:Voter ID:e94eaff8-43de-4533-9f41-13ae882abf49 Address:10.10.10.15:8300}] latest_configuration_index = 0 num_peers = 2 protocol_version = 3 protocol_version_max = 3 protocol_version_min = 0 snapshot_version_max = 1 snapshot_version_min = 0 state = Follower term = 2 runtime: arch = amd64 cpu_count = 16 goroutines = 103 max_procs = 16 os = linux version = go1.15.6 serf_lan: coordinate_resets = 0 encrypted = true event_queue = 0 event_time = 2 failed = 0 health_score = 0 intent_queue = 0 left = 0 member_time = 19 members = 9 query_queue = 0 query_time = 1 serf_wan: coordinate_resets = 0 encrypted = true event_queue = 0 event_time = 1 failed = 0 health_score = 0 intent_queue = 0 left = 0 member_time = 4 members = 3 query_queue = 0 query_time = 1

</details>

### Operating system and Environment details

Debian GNU/Linux 10.9, amd64

### Log Fragments

Below a piece of Nginx logs:

11.22.33.44 - - [10/Jun/2021:07:26:30 +0000] "GET /foo/ui/ HTTP/1.1" 200 3429 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0" "-" 11.22.33.44 - - [10/Jun/2021:07:26:37 +0000] "GET /v1/catalog/datacenters HTTP/1.1" 404 136 "https://consul.example.com/foo/ui/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0" "-"

and an error log in my Firefox web developer console:

Error while processing route: index The adapter could not find the resource r@https://consul.example.com/foo/ui/assets/vendor-4f6b2352943214ccc7e7113ab286a174.js:6280:19 r@https://consul.example.com/foo/ui/assets/vendor-4f6b2352943214ccc7e7113ab286a174.js:6284:54 value@https://consul.example.com/foo/ui/assets/consul-ui-ee55527e9defc2d4261720f39a9f7525.js:131:12 value/<@https://consul.example.com/foo/ui/assets/consul-ui-ee55527e9defc2d4261720f39a9f7525.js:123:158 promise callbackvalue@https://consul.example.com/foo/ui/assets/consul-ui-ee55527e9defc2d4261720f39a9f7525.js:123:130 j@https://consul.example.com/foo/ui/assets/consul-ui-ee55527e9defc2d4261720f39a9f7525.js:112:10 value@https://consul.example.com/foo/ui/assets/consul-ui-ee55527e9defc2d4261720f39a9f7525.js:139:139 tt</f._query/</s<@https://consul.example.com/foo/ui/assets/vendor-4f6b2352943214ccc7e7113ab286a174.js:7190:62 b@https://consul.example.com/foo/ui/assets/vendor-4f6b2352943214ccc7e7113ab286a174.js:4777:12 g/<@https://consul.example.com/foo/ui/assets/vendor-4f6b2352943214ccc7e7113ab286a174.js:4784:28 h</t.invoke@https://consul.example.com/foo/ui/assets/vendor-4f6b2352943214ccc7e7113ab286a174.js:4298:163 h</t.flush@https://consul.example.com/foo/ui/assets/vendor-4f6b2352943214ccc7e7113ab286a174.js:4290:74 p</t.flush@https://consul.example.com/foo/ui/assets/vendor-4f6b2352943214ccc7e7113ab286a174.js:4304:207 H</r._end@https://consul.example.com/foo/ui/assets/vendor-4f6b2352943214ccc7e7113ab286a174.js:4367:9 e/this._boundAutorunEnd@https://consul.example.com/foo/ui/assets/vendor-4f6b2352943214ccc7e7113ab286a174.js:4318:574 promise callbacki/<@https://consul.example.com/foo/ui/assets/vendor-4f6b2352943214ccc7e7113ab286a174.js:4274:28 flush@https://consul.example.com/foo/ui/assets/vendor-4f6b2352943214ccc7e7113ab286a174.js:2936:248 H</r._scheduleAutorun@https://consul.example.com/foo/ui/assets/vendor-4f6b2352943214ccc7e7113ab286a174.js:4381:3 H</r._ensureInstance@https://consul.example.com/foo/ui/assets/vendor-4f6b2352943214ccc7e7113ab286a174.js:4379:86 H</r.schedule@https://consul.example.com/foo/ui/assets/vendor-4f6b2352943214ccc7e7113ab286a174.js:4339:13 e.schedule@https://consul.example.com/foo/ui/assets/vendor-4f6b2352943214ccc7e7113ab286a174.js:2928:204 waitForDOMReady@https://consul.example.com/foo/ui/assets/vendor-4f6b2352943214ccc7e7113ab286a174.js:2684:132 init@https://consul.example.com/foo/ui/assets/vendor-4f6b2352943214ccc7e7113ab286a174.js:2683:425 r@https://consul.example.com/foo/ui/assets/vendor-4f6b2352943214ccc7e7113ab286a174.js:2485:9 v@https://consul.example.com/foo/ui/assets/vendor-4f6b2352943214ccc7e7113ab286a174.js:2396:123 y</e.create@https://consul.example.com/foo/ui/assets/vendor-4f6b2352943214ccc7e7113ab286a174.js:2405:108 @https://consul.example.com/foo/ui/assets/consul-ui-ee55527e9defc2d4261720f39a9f7525.js:6859:193

and finally the reason in an appropriate `.js` file:

$ curl -s -n https://consul.example.com/foo/ui/assets/consul-ui-ee55527e9defc2d4261720f39a9f7525.js -o - |grep /v1/catalog/datacenters return Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}(["\n GET /v1/catalog/datacenters\n "]) $


### How to fix it?

See below how I fix the issue using Nginx `sub_filter` rules:
    location /foo/ui/ {
            sub_filter_once  off;
            sub_filter_types application/javascript text/javascript;
            sub_filter       'rootURL%22%3A%22/ui/%' 'rootURL%22%3A%22/foo/%';
            sub_filter       'namespace:"v1"' 'namespace:"foo/v1"';
            sub_filter       '/v1/catalog/' '/foo/v1/catalog/';
            sub_filter       '/v1/internal/' '/foo/v1/internal/';
    }

but IMHO the code of `.js` files should respect `-ui-content-path` flag too.
johncowen commented 3 years ago

Hi @ptecza

Thanks for submitting the issue, its good to hear that you are interested in this functionality.

The -ui-content-path/content_path configuration flag/options is only meant for changing the path where Consul serves the UI from, not the API. The UI will still look for the API at /v1.

https://www.consul.io/docs/agent/options#ui_config_content_path

content_path - This specifies the HTTP path that the web UI should be served from. Defaults to /ui/. Equivalent to the -ui-content-path flag.

-ui-content-path - This flag provides the option to change the path the Consul UI loads from and will be displayed in the browser. By default, the path is /ui/, for example http://localhost:8500/ui/. Only alphanumerics, -, and _ are allowed in a custom path. /v1/ is not allowed as it would overwrite the API endpoint.

We are aware that there are a fair amount of feature requests/GH issues for allowing the UI to run with a configurable API path and we are actively discussing it as a team, for example see https://github.com/hashicorp/consul/issues/9829#issuecomment-857685303, whether that involves making the API requests in the UI respect the -ui-content-path too or whether it could involve a separate flag (or something else) is still under to discussion. Also fairly related https://github.com/hashicorp/consul/issues/10354

ptecza commented 3 years ago

Hello @johncowen

Thanks a lot for your response! I'm keeping my fingers crossed for taking the best decision by your team :)