DiscourseをNginxではなくApacheのvhostで実行する方法

Discourse をデフォルトの Nginx ではなく、Apache の仮想ホスト上で正常にセットアップできた方はいますか?

これらのフォーラムで Apache に関する議論の多くは、すでに Apache が動作しているホスト上で Discourse を実行するものについてです。私が目にした限り、すべてのケースで Apache を Nginx にリバースプロキシする構成になっています。明確にしておきますが、私は Docker ベンダーコンテナ内で Discourse を実行する際、Nginx ではなく Apache で仮想ホストを動かしたいと考えています。

具体的には、mod_security を有効にするために、Discourse のバックエンドを Nginx ではなく Apache で実行したいと考えています。Nginx で mod_security を使用するには、Nginx をソースからコンパイルする必要があり、その複雑さを避けたいのです。

現在のプロダクションサーバーでは、すでに複数のサイト(WordPress、MediaWiki など)を運用しています。HTTPS の終端と基本的な DoS 対策としてのレート制限には Nginx を使用し、その後 Varnish キャッシュを経由して mod_security を備えた Apache バックエンドへリクエストを転送する構成です。可能であれば、Discourse でもこのアーキテクチャを維持したいと考えています。つまり、バックエンドの Discourse Docker コンテナ内で Nginx ではなく、mod_security を備えた Apache を実行したいのです。

Discourse を Apache 上で実行する構成に成功した方はいますか?

Sorry, I’m new to docker. I’m really struggling to find how the ‘/etc/nginx/conf.d/discourse.conf’ file is even put in-place on the docker container. Can someone elucidate the steps for how that file is generated and put in-place on the container by the ‘./launcher’ script?

EDIT: wait, is nginx already being compiled from source by Discourse?

No they have not. Discourse is rather tightly connected with nginx. It will be difficult if impossible to replace it with apache. And since you’ll be the only person doing that, no one else will care when it breaks.

Just use the docker container that everyone on the planet uses and stick apache in front of it if you think that’ll help security somehow.

It does look like it.

So it looks like /etc/nginx/conf.d/discourse.conf is generated on the container by the template file /var/docker/templates/web.template.yml – which copies it in-place from $home/config/nginx.sample.conf, and then makes changes with the replace yaml blocks (which is further updated in subsequent templates, such as templates/web.socketed.template.yml).

I was assuming that the nginx.sample.conf file was just installed in-place by nginx when compiling from source in image/base/install-nginx, but the only nginx.sample.conf file I found on the container (/var/www/discourse/config/nginx.sample.conf) already had discourse-specific directives in it.

Where does nginx.sample.conf come from?

I’d rather not have nginx → varnish → apache → nginx :slight_smile:

To be clear: do you think it would be safer for me to update the ‘image/base/install-nginx’ script to compile nginx on the Discourse docker container with mod_security support rather than update the container to run the Discourse vhost behind apache (as opposed to Nginx)? If so, what are the risks of me modifying the install-nginx script? How could it break my Discourse install in the future?

PSA: Everything you are proposing to do, while technically feasible is completely unsupported. We can’t reasonably support every possible combination people come up with to serve Discourse on the web. So the support here on this forums are restricted to installs following the discourse/docs/INSTALL-cloud.md at main · discourse/discourse · GitHub guide.

discourse/config/nginx.sample.conf at main · discourse/discourse · GitHub

It will, eventually, break in many ways. You will be on the hook on merging with upstream changes continually and handling your fork forever.

「いいね!」 7

Perfect, thank you! Wow, it’s a totally different repo. How does the container get that file from the other repo? Is it here?

And what is $home?

You can read the source code to find all that and more at discourse_docker/image/base/Dockerfile at master · discourse/discourse_docker · GitHub

「いいね!」 4

I resolved setting app.yml (Discouse) like so:
expose:
- "2045:80" # http
- "8443:443" # https

… and my vhost like so:
<VirtualHost *myhostIP*:443>
ServerName forum.domain.org
ServerAlias forum.domain.org

ProxyAddHeaders Off
ProxyPass / "http://localhost:2045/"
ProxyPassReverse / "http://localhost:2045/"
</VirtualHost>

I hope I didn’t misunderstand your question :blush:

「いいね!」 1

@ledimeo That’s what @pfaffman suggested, and what I think would be better in this case, to not break things badly in the future when new versions are released. But it seems that he already uses another nginx at the front as a reverse proxy and don’t want:

That said, I think that proxing apache to nginx (even if it would end up with those 4 layers) would at least be more maintainable than trying to change the way discourse works in the official installation.

For this purpose, you should mostly pretend that nginx is inside the “black box” of the docker container and not concern yourself with it.

So: nginx → varnish → apache → Discourse (comprising nginx and unicorn)

(except for configuring it to trust upstream proxy IPs)

「いいね!」 3

So adding apache as a proxy to the Discourse nginx is definitely an option.

I agree that a pro would be making future upgrades easier, and that’s an important point.

But adding another hop to the architecture would not only make debugging issues in the future more complex – I also have concerns about Apache’s performance as a proxy to a web application that uses long polling, as pointed out by @sam in this post from 2016.

I generally prefer nginx to apache, except when it comes to mod_security. It would be fantastic if the OS repos included packages to enable mod_security in nginx like they do for Apache, but currently enabling mod_security on nginx requires compiling nginx from source in both RHEL/Cent and Debian. And I avoid depending on packages compiled from source on production like the plague…

related: I’ve been poking around with the install-nginx script, and I found a minor logic error: the cleanup line that’s supposed to delete the nginx source from /tmp/ actually doesn’t do anything.

There is no /tmp/nginx/ directory: it’s /tmp/nginx-$VERSION/ (at the moment it’s /tmp/nginx-1.17.4/ and there’s also the tarball /tmp/nginx-1.17.4.tar.gz).

I think you mean this instead:

rm -fr /tmp/nginx-${VERSION}*

Unfortunately, my updated /var/discourse/image/base/install-nginx script does not appear to be executed when I run a /var/discourse/launcher destroy app && /var/discourse/launcher/rebuild app.

Is there any reason this rebuild command would not re-execute the updated install-nginx script?

How are you amending the script after the rebuild? With a hook?

vim /var/discourse/image/base/install-nginx

After a rebuild can you still see your changes? Editing files in the image directly like that won’t work.

You need to use hooks to amend the file from your app.yml

Have you worked with docker before?

「いいね!」 1

If you aren’t familiar with Docker this is going to be a hard path…

discourse_docker contains the source code for our base docker image that lives in the public Docker registry, and that will never be run locally and the whole reason is having a reusable image.

「いいね!」 1

ok. Well, I lied about vim for simplicity. I’m actually running these commands to idempotently & robustly update the script

cd /var/discourse/image/base
cp install-nginx install-nginx.`date "+%Y%m%d_%H%M%S"`.orig

# add a block to checkout the the modsecurity nginx module just before downloading the nginx source
grep 'ModSecurity' install-nginx || sed -i 's%\(curl.*nginx\.org/download.*\)%# mod_security --maltfield\napt-get install -y libmodsecurity-dev modsecurity-crs\ncd /tmp\ngit clone --depth 1 https://github.com/SpiderLabs/ModSecurity-nginx.git\n\n\1%' install-nginx

# update the configure line to include the ModSecurity module checked-out above
sed -i '/ModSecurity/! s%^[^#]*./configure \(.*nginx.*\)%#./configure \1\n./configure \1 --add-module=/tmp/ModSecurity-nginx%' install-nginx

# add a line to cleanup section
grep 'rm -fr /tmp/ModSecurity-nginx' install-nginx || sed -i 's%\(rm -fr.*/tmp/nginx.*\)%rm -fr /tmp/ModSecurity-nginx\n\1%' install-nginx

Where should I put these commands so that the actual install-nginx script used by the container on bootstrap is modified?