camo: Replace with go-camo implementation.

The upstream of the `camo` repository[1] has been unmaintained for
several years, and is now archived by the owner.  Additionally, it has
a number of limitations:
 - It is installed as a sysinit service, which does not run under
   Docker
 - It does not prevent access to internal IPs, like 127.0.0.1
 - It does not respect standard `HTTP_proxy` environment variables,
   making it unable to use Smokescreen to prevent the prior flaw
 - It occasionally just crashes, and thus must have a cron job to
   restart it.

Swap camo out for the drop-in replacement go-camo[2], which has the
same external API, requiring not changes to Django code, but is more
maintained.  Additionally, it resolves all of the above complaints.

go-camo is not configured to use Smokescreen as a proxy, because its
own private-IP filtering prevents using a proxy which lies within that
IP space.  It is also unclear if the addition of Smokescreen would
provide any additional protection over the existing IP address
restrictions in go-camo.

go-camo has a subset of the security headers that our nginx reverse
proxy sets, and which camo set; provide the missing headers with `-H`
to ensure that go-camo, if exposed from behind some other non-nginx
load-balancer, still provides the necessary security headers.

Fixes #18351 by moving to supervisor.
Fixes zulip/docker-zulip#298 also by moving to supervisor.

[1] https://github.com/atmos/camo
[2] https://github.com/cactus/go-camo
This commit is contained in:
Alex Vandiver 2021-11-18 00:44:51 +00:00 committed by Alex Vandiver
parent c33562f0a8
commit b982222e03
7 changed files with 38 additions and 31 deletions

View File

@ -239,7 +239,7 @@ strength allowed is controlled by two settings in
browser is logged into a Zulip account that has received the browser is logged into a Zulip account that has received the
uploaded file in question). uploaded file in question).
- Zulip supports using the Camo image proxy to proxy content like - Zulip supports using the [go-camo][go-camo] image proxy to proxy content like
inline image previews, that can be inserted into the Zulip message feed by inline image previews, that can be inserted into the Zulip message feed by
other users. This ensures that clients do not make requests to external other users. This ensures that clients do not make requests to external
servers to fetch images, improving privacy. servers to fetch images, improving privacy.
@ -266,6 +266,7 @@ strength allowed is controlled by two settings in
internal corporate network. The default Smokescreen configuration internal corporate network. The default Smokescreen configuration
denies access to all non-public IP addresses, including 127.0.0.1. denies access to all non-public IP addresses, including 127.0.0.1.
[go-camo]: https://github.com/cactus/go-camo
[ssrf]: https://owasp.org/www-community/attacks/Server_Side_Request_Forgery [ssrf]: https://owasp.org/www-community/attacks/Server_Side_Request_Forgery
[smokescreen-setup]: ../production/deployment.html#customizing-the-outgoing-http-proxy [smokescreen-setup]: ../production/deployment.html#customizing-the-outgoing-http-proxy

View File

@ -1,22 +1,35 @@
class zulip::camo { class zulip::camo {
$camo_packages = [# Needed for camo # TODO/compatibility: Removed 2021-11 in version 5.0; these lines
'nodejs', # can be removed once one must have upgraded through Zulip 5.0 or
'camo', # higher to get to the next release.
] package { 'camo':
package { $camo_packages: ensure => 'installed' } ensure => 'purged',
}
$version = '2.3.0'
$dir = "/srv/zulip-go-camo-${version}/"
$bin = "${dir}bin/go-camo"
zulip::external_dep { 'go-camo':
version => $version,
url => "https://github.com/cactus/go-camo/releases/download/v${version}/go-camo-${version}.go1171.linux-amd64.tar.gz",
sha256 => '965506e6edb9d974c810519d71e847afb7ca69d1d01ae7d8be6d7a91de669c0c',
tarball_prefix => "go-camo-${version}/",
bin => 'bin/go-camo',
}
$camo_key = zulipsecret('secrets', 'camo_key', '') $camo_key = zulipsecret('secrets', 'camo_key', '')
file { "${zulip::common::supervisor_conf_dir}/go-camo.conf":
file { '/etc/default/camo':
ensure => file, ensure => file,
require => Package[camo], require => [
Package['camo'],
Package[supervisor],
File[$bin],
],
owner => 'root', owner => 'root',
group => 'root', group => 'root',
mode => '0644', mode => '0644',
content => template('zulip/camo_defaults.template.erb'), content => template('zulip/supervisor/go-camo.conf.erb'),
notify => Service[camo], notify => Service[supervisor],
}
service { 'camo':
ensure => running,
} }
} }

View File

@ -13,10 +13,6 @@ class zulip::profile::standalone {
include zulip::profile::redis include zulip::profile::redis
include zulip::profile::memcached include zulip::profile::memcached
include zulip::profile::rabbitmq include zulip::profile::rabbitmq
if $::osfamily == debian { include zulip::localhost_camo
# camo is only required on Debian-based systems as part of
# our migration towards not including camo at all.
include zulip::localhost_camo
}
include zulip::static_asset_compiler include zulip::static_asset_compiler
} }

View File

@ -1,3 +0,0 @@
ENABLED=yes
PORT=9292
CAMO_KEY=<%= scope["zulip::camo::camo_key"] %>

View File

@ -0,0 +1,9 @@
[program:go-camo]
command=<%= @bin %> --listen=0.0.0.0:9292 -H "Strict-Transport-Security: max-age=15768000" -H "X-Frame-Options: DENY" --verbose
environment=GOCAMO_HMAC="<%= @camo_key %>"
priority=15
autostart=true
autorestart=true
user=zulip
redirect_stderr=true
stdout_logfile=/var/log/zulip/camo.log

View File

@ -1 +0,0 @@
* * * * * root /bin/bash -c '(pgrep -u nobody -F /var/run/camo.pid || /etc/init.d/camo restart) 2>&1 >>/var/log/camo/restart-log'

View File

@ -2,12 +2,4 @@ class zulip_ops::camo {
include zulip::camo include zulip::camo
zulip_ops::firewall_allow { 'camo': port => '9292' } zulip_ops::firewall_allow { 'camo': port => '9292' }
file { '/etc/cron.d/camo':
ensure => file,
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/zulip_ops/cron.d/camo',
}
} }