codimd icon indicating copy to clipboard operation
codimd copied to clipboard

docker image config to serve web files from subdir instead of root

Open aleqx opened this issue 5 years ago • 10 comments
trafficstars

I want to use the Docker installation rather than manual installation (docker-compose image works fine, no problem accessing it under port 3000) but I want to change CodiMD's web installation folder from root to a subdir, i.e. from  http://localhost:3000/  to  http://localhost:3000/codimd -- is that possible? Any pointers?

Right now I notice the html content points to absolute urls such as  <img src="/screenshot.png" /> .

I take it I'd need to edit the docker image to change the internal web server's config to serve the static files at the /codimd url instead of at /, and then also set CMD_URL_PATH=codimd in the docker env variables.

  • Setting only CMD_URL_PATH=codimd does changes the html output of localhost:3000/ and shows broken images and css, there is nothing at localhost:3000/codimd

  • Setting only CMD_URL_PATH=codimd and also changing app.js to read app.use('/codimd', express.static(path.join(__dirname, '/public'), { maxAge: config.staticCacheTime, index: false })) ends up in a redirect loop between /codimd/ and /codimd` (not the missing trailing slash)

I'd be grateful for some more exact pointers on how to do this.

The end goal is to put the CodiMD docker under a proxypass such that mydomain.com/codimd proxy-passes to localhost:3000/codimd (I can do a few things by rewriting html output, but that's not cutting it, as some CSS would also need to, it would be just cleaner to have CodiMD installed in a web subdir)

aleqx avatar Mar 03 '20 02:03 aleqx

Hi @aleqx, sorry for late reply.

Thanks for you reported this issue about sub-folder deployment. We found some resource path not follow CMD_URL_PATH (config file and fonts)and we're going to fix this problem on the next version (v2.0.1).

Before we release next version, we provide a workaround setting in apache to fix this problem. (please tell me if you use others web server, I can test it)

But it has some limits:

  1. You didn't used route https://md.example.com/config on root directory.
  2. You didn't used any files under https://md.example.com/build/ and https://md.example.com/fonts/.

Workaround

The workaround setup CodiMD on https://md.example.com/internal-codimd/, and proxy all request to backend CodiMD server http://172.16.30.24:3000/.

1. CodiMD Server

CodiMD server is setup by docker and host on 172.16.30.24. The docker-compose config is shown as below.

version: "3"
services:
  database:
    image: postgres:11.6-alpine
    environment:
      - POSTGRES_USER=codimd
      - POSTGRES_PASSWORD=change_password
      - POSTGRES_DB=codimd
    volumes:
      - "database-data:/var/lib/postgresql/data"
    restart: always
  codimd:
    # you can use image or custom build below
    image: nabo.codimd.dev/hackmdio/hackmd:2.0.0
    environment:
      - CMD_DB_URL=postgres://codimd:change_password@database/codimd
      - CMD_USECDN=false
      - CMD_DOMAIN=md.example.com
      - CMD_URL_PATH=internal-codimd
      - CMD_PROTOCOL_USESSL=true
    depends_on:
      - database
    ports:
      - "3000:3000"
    volumes:
      - upload-data:/home/hackmd/app/public/uploads
    restart: always
volumes:
  database-data: {}
  upload-data: {}

2. Apache config

The apache requires these modules to apply the config shown as below

  • For SSL
    • mod_ssl
    • mod_socache_shmcb
  • For Reverse Proxy
    • mod_proxy
    • mod_proxy_http
    • mod_proxy_wstunnel
    • mod_rewrite
# Redirect http to https
<VirtualHost *:80>
	# ServerName md.example.com
	Redirect permanent / https://md.example.com/
</VirtualHost>

# https
<IfModule mod_ssl.c>
    <VirtualHost *:443>
        ServerName md.example.com

        # SSL Config
        SSLEngine on
        SSLCertificateKeyFile /usr/local/apache2/certs/md.example.com/cert-key.pem
        SSLCertificateFile /usr/local/apache2/certs/md.example.com/cert.pem

        RewriteEngine on
        RewriteCond %{REQUEST_URI} ^/internal-codimd/socket.io             [NC]
        RewriteCond %{QUERY_STRING} transport=websocket    [NC]
        RewriteRule /internal-codimd/(.*)  ws://172.16.30.24:3000/$1          [P,L]

        ProxyPass /internal-codimd/ http://172.16.30.24:3000/
        
        # temporary workaround for /confg, /build/* and /fonts/*
        ProxyPass /config http://172.16.30.24:3000/config
        ProxyPass /build http://172.16.30.24:3000/build
        ProxyPass /fonts http://172.16.30.24:3000/fonts
    </VirtualHost>
</IfModule>

Finally, I create a repository for demostraction (https://github.com/a60814billy/codimd-subdir-workaround), you can see more detailed configuration in the repo.

a60814billy avatar Mar 05 '20 19:03 a60814billy

I can confirm this error is still present in 2.0.1. It can be resolved in the same manner as descibred above, hosting on docker with a proxy on domain.example/codimd and exposing the config file, as well as the build and fonts folder on domain.example/

nmaas87 avatar Apr 20 '20 13:04 nmaas87

That's not a workaround for me. It already works on a subdomain (e.g. md.example.com) but that's not what I want. What I want is example.com/md.

aleqx avatar Apr 20 '20 13:04 aleqx

Its definitly a bug and without the possibilty to host parts of the running containers in the root directory, its not possible to get it working.

nmaas87 avatar Apr 20 '20 14:04 nmaas87

Its definitly a bug and without the possibilty to host parts of the running containers in the root directory, its not possible to get it working.

Not sure I understand that correctly. I don't care where the container hosts its parts locally in localhost (i'm happy to make the container host in any manner needed) ... i just want the public url to be https://example.com/md (i do not want a subdomain https://md.example.com).

Is the above possible or not?

It woould be possible if the localhost docker serves files correctly from localhost:3000/md (which is the bug i reported) so i can proxypass to it. Otherwise css/js/etc files inside the served html will still point to / instead of /md. It doesn't seem like the bug I reported was fixed despite the claim it's fixed in 2.0.1

aleqx avatar Apr 20 '20 15:04 aleqx

You're right. Its not fixed. To get it working in its current situation, you would need to do three things: 1.) with your docker container running, go to the correct path, i.e. example.com/md/config - it will show the config file, which it tries to find normally in example.com/config. Save this file and make it available on example.com 2.) get into your running docker container and copy the whole /home/hackmd/app/public/build out of the container (i.e. via the uploads folder?) and make it available on example.com/build 3.) do the same for the fonts folder ( /home/hackmd/app/public/fonts).

With that, your container should be working, so going to https://example.com/md should work, as the required files (config file, build folder and fonts folder) are searched in https://example.com/ and are now available to load at this place.

Yes, its a really dirty workaround and basically the same as descibred by https://github.com/hackmdio/codimd/issues/1440#issuecomment-595397057 - only expressed in other wording and mines a lot more dirty....

No, this issue here has not been resolved in 2.0.1 and it still needs fxing.

nmaas87 avatar Apr 21 '20 06:04 nmaas87

I found that codimd/server has solutions for this issue.

  • https://github.com/codimd/server/commit/2acd54bbdb91afcadcef5ae886118eb55be535b8
  • https://github.com/codimd/server/commit/2d3b009e13d530b902e7c36e62134a8e72c26ab4
  • https://github.com/codimd/server/commit/23c7b5b0a6a4bb6ba79eb8ee61346de623f7aced

I tried codimd/container 1.6.0, nginx as reverse proxy, and to serve codimd on /codimd subpath.

The sample docker-compose.yml:

version: "3"
services:
  database:
    image: postgres:11.6-alpine
    environment:
      - POSTGRES_USER=codimd
      - POSTGRES_PASSWORD=badpassword
      - POSTGRES_DB=codimd
    volumes:
      - "database-data:/var/lib/postgresql/data"
    restart: always
  codimd:
    image: quay.io/codimd/server:1.6.0-alpine
    environment:
      - CMD_DB_URL=postgres://codimd:badpassword@database/codimd
      - CMD_USECDN=false
      - CMD_URL_PATH=hackmd
      - CMD_PORT=3000
    depends_on:
      - database
    volumes:
      - upload-data:/home/hackmd/app/public/uploads
    restart: always
  nginx:
    image: nginx:1.18.0-alpine
    environment:
      - NGINX_HOST=localhost
      - NGINX_PORT=80
    depends_on:
      - codimd
    ports:
      - "80:80"
    volumes:
      - './nginx-default.conf:/etc/nginx/conf.d/default.conf'
volumes:
  database-data: {}
  upload-data: {}

The sample nginx-default.conf:

  listen 80 default_server;
  server_name localhost;

  #charset koi8-r;
  #access_log  /var/log/nginx/host.access.log  main;

  location / {
    root   /usr/share/nginx/html;
    index  index.html index.htm;
  }

  error_page   500 502 503 504  /50x.html;
  location = /50x.html {
    root   /usr/share/nginx/html;
  }

  location ^~ /hackmd/ {
    proxy_pass http://codimd:3000/;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
  }
}

These fixes work like a charm:

Screenshot from 2020-05-20 11-49-38

tarlety avatar May 20 '20 03:05 tarlety

@aleqx

Is this deployment case as expected?

If yes, I can help to port code and file PR for this issue.

tarlety avatar May 28 '20 04:05 tarlety

Hello, is there any update here?

yzx9 avatar Sep 19 '23 09:09 yzx9