~
This commit is contained in:
parent
dc4d777962
commit
4a08968827
4 changed files with 347 additions and 1 deletions
|
@ -10,6 +10,7 @@
|
||||||
imports = [
|
imports = [
|
||||||
./hardware-configuration.nix
|
./hardware-configuration.nix
|
||||||
./disks.nix
|
./disks.nix
|
||||||
|
./zulip
|
||||||
];
|
];
|
||||||
|
|
||||||
boot.loader.systemd-boot.enable = true;
|
boot.loader.systemd-boot.enable = true;
|
||||||
|
@ -88,7 +89,6 @@
|
||||||
virtualHosts = {
|
virtualHosts = {
|
||||||
"son.katvayor.net" = {
|
"son.katvayor.net" = {
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
addSSL = true;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
41
machines/kat-son/zulip/default.nix
Normal file
41
machines/kat-son/zulip/default.nix
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
{ pkgs, ... }:
|
||||||
|
let
|
||||||
|
home = "/var/lib/zulip";
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [ ./postgres.nix ./nginx.nix ];
|
||||||
|
|
||||||
|
# profile/base.pp
|
||||||
|
users = {
|
||||||
|
groups.zulip = { };
|
||||||
|
users.zulip = {
|
||||||
|
isSystemUser = true;
|
||||||
|
group = "zulip";
|
||||||
|
inherit home;
|
||||||
|
homeMode = "755";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd = {
|
||||||
|
tmpfiles.rules = [
|
||||||
|
''f ${home}/zulip.conf 644 zulip zulip ""''
|
||||||
|
''f ${home}/settings.py 644 zulip zulip ""''
|
||||||
|
''f ${home}/zulip-secrets.conf 640 zulip zulip ""''
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
#security.pam.loginLimits = [
|
||||||
|
# {
|
||||||
|
# domain = "zulip";
|
||||||
|
# type = "soft";
|
||||||
|
# item = "nofile";
|
||||||
|
# value = "1000000";
|
||||||
|
# }
|
||||||
|
# {
|
||||||
|
# domain = "zulip";
|
||||||
|
# type = "hard";
|
||||||
|
# item = "nofile";
|
||||||
|
# value = "1048576";
|
||||||
|
# }
|
||||||
|
#];
|
||||||
|
}
|
278
machines/kat-son/zulip/nginx.nix
Normal file
278
machines/kat-son/zulip/nginx.nix
Normal file
|
@ -0,0 +1,278 @@
|
||||||
|
{ pkgs, lib, ... }:
|
||||||
|
let
|
||||||
|
zulip-includes = pkgs.stdenv.mkDerivation rec {
|
||||||
|
pname = "zulip-include";
|
||||||
|
version = "8.4";
|
||||||
|
|
||||||
|
src = pkgs.fetchFromGitHub {
|
||||||
|
owner = "zulip";
|
||||||
|
repo = "zulip";
|
||||||
|
rev = version;
|
||||||
|
hash = "sha256-wsdVlJ0RDWsKwpvT0TsqqT8v5bubjsPDaGebRiIoQoQ=";
|
||||||
|
};
|
||||||
|
installPhase = ''
|
||||||
|
mkdir $out
|
||||||
|
cp puppet/zulip/files/nginx/{dhparam.pem,uwsgi_params,zulip-include-frontend/*,zulip-include-common/*} $out
|
||||||
|
sed -i "s/\/etc\/nginx\/zulip-include/\/nix\/store\/$(basename $out)/" $out/*
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
host = "son.katvayor.net";
|
||||||
|
home = "/var/lib/zulip";
|
||||||
|
loadbalancers = [ ];
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# nginx.pp
|
||||||
|
services.nginx = {
|
||||||
|
enable = true;
|
||||||
|
recommendedGzipSettings = true;
|
||||||
|
sslDhparam = zulip-includes + "/dhparam.pem";
|
||||||
|
upstreams = {
|
||||||
|
django.servers."unix:${home}/deployments/uwsgi-socket" = { };
|
||||||
|
localhost_sso.servers."127.0.0.1:8888" = { };
|
||||||
|
camo.servers."127.0.0.1:9292" = { };
|
||||||
|
# TODO later : mess with tornado
|
||||||
|
tornado.servers."127.0.0.1:9800" = {
|
||||||
|
keepalive = 10000;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
commonHttpConfig =
|
||||||
|
let
|
||||||
|
trusted-proto =
|
||||||
|
if loadbalancers == [ ] then
|
||||||
|
''
|
||||||
|
map $remote_addr $trusted_x_forwarded_proto {
|
||||||
|
default $scheme;
|
||||||
|
}
|
||||||
|
map $http_x_forwarded_for $x_proxy_misconfiguration {
|
||||||
|
default "";
|
||||||
|
"~." "No proxies configured in Zulip, but proxy headers detected from proxy at $remote_addr; see https://zulip.readthedocs.io/en/latest/production/reverse-proxies.html";
|
||||||
|
}
|
||||||
|
''
|
||||||
|
else
|
||||||
|
''''; # TODO later : loadbalancers
|
||||||
|
tornado_map = # TODO later : mess with tornado
|
||||||
|
''
|
||||||
|
map "" $tornado_server {
|
||||||
|
default http://tornado;
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
|
||||||
|
in
|
||||||
|
''
|
||||||
|
${trusted-proto}
|
||||||
|
${tornado_map}
|
||||||
|
'';
|
||||||
|
virtualHosts.${host} = {
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
extraConfig = ''
|
||||||
|
error_page 502 503 504 /static/webpack-bundles/5xx.html;
|
||||||
|
'';
|
||||||
|
locations = {
|
||||||
|
# app
|
||||||
|
"/local-static/" = {
|
||||||
|
alias = "${home}/local-static/";
|
||||||
|
};
|
||||||
|
"/static/" = {
|
||||||
|
alias = "${home}/prod-static/";
|
||||||
|
extraConfig = ''
|
||||||
|
include ${zulip-includes}/headers;
|
||||||
|
add_header Access-Control-Allow-Origin *;
|
||||||
|
add_header Timing-Allow-Origin *;
|
||||||
|
error_page 404 /django_static_404.html;
|
||||||
|
location ~ '\.[0-9a-f]{12}\.|[./][0-9a-f]{20}\.' {
|
||||||
|
include ${zulip-includes}/headers;
|
||||||
|
add_header Access-Control-Allow-Origin *;
|
||||||
|
add_header Timing-Allow-Origin *;
|
||||||
|
add_header Cache-Control "public, max-age=31536000, immutable";
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"/json/events" = {
|
||||||
|
extraConfig = ''
|
||||||
|
if ($request_method = 'OPTIONS') {
|
||||||
|
# add_header does not propagate into/out of blocks, so this
|
||||||
|
# include cannot be factored out
|
||||||
|
include ${zulip-includes}/headers;
|
||||||
|
add_header Allow 'OPTIONS, GET, DELETE' always;
|
||||||
|
return 204;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request_method !~ ^(GET|DELETE)$ ) {
|
||||||
|
# add_header does not propagate into/out of blocks, so this
|
||||||
|
# include cannot be factored out
|
||||||
|
include ${zulip-includes}/headers;
|
||||||
|
add_header Allow 'OPTIONS, GET, DELETE' always;
|
||||||
|
return 405;
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy_pass $tornado_server;
|
||||||
|
include ${zulip-includes}/proxy_longpolling;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"/api/v1/events" = {
|
||||||
|
extraConfig = ''
|
||||||
|
if ($request_method = 'OPTIONS') {
|
||||||
|
include ${zulip-includes}/tornado_cors_headers;
|
||||||
|
add_header Allow 'OPTIONS, GET, DELETE' always;
|
||||||
|
return 204;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request_method !~ ^(GET|DELETE)$ ) {
|
||||||
|
include ${zulip-includes}/headers;
|
||||||
|
add_header Allow 'OPTIONS, GET, DELETE' always;
|
||||||
|
return 405;
|
||||||
|
}
|
||||||
|
|
||||||
|
include ${zulip-includes}/tornado_cors_headers;
|
||||||
|
proxy_pass $tornado_server;
|
||||||
|
include ${zulip-includes}/proxy_longpolling;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"~ ^/internal/tornado/(\\d+)(/.*)$" = {
|
||||||
|
extraConfig = ''
|
||||||
|
internal;
|
||||||
|
proxy_pass http://tornado$1$2$is_args$args;
|
||||||
|
include ${zulip-includes}/proxy_longpolling;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"/" = {
|
||||||
|
extraConfig = ''
|
||||||
|
include ${zulip-includes}/uwsgi_params;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"/thumbnail" = {
|
||||||
|
extraConfig = ''
|
||||||
|
include ${zulip-includes}/api_headers;
|
||||||
|
include ${zulip-includes}/uwsgi_params;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"/avatar" = {
|
||||||
|
extraConfig = ''
|
||||||
|
include ${zulip-includes}/api_headers;
|
||||||
|
include ${zulip-includes}/uwsgi_params;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"/user_uploads" = {
|
||||||
|
extraConfig = ''
|
||||||
|
include ${zulip-includes}/api_headers;
|
||||||
|
include ${zulip-includes}/uwsgi_params;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"/api/" = {
|
||||||
|
extraConfig = ''
|
||||||
|
include ${zulip-includes}/api_headers;
|
||||||
|
include ${zulip-includes}/uwsgi_params;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
# uploads-internal
|
||||||
|
"~ ^/internal/s3/(?<s3_hostname>[^/\\s]+)/(?<s3_path>[^\\s]*)" = {
|
||||||
|
extraConfig = ''
|
||||||
|
internal;
|
||||||
|
include ${zulip-includes}/headers;
|
||||||
|
add_header Content-Security-Policy "default-src 'none'; media-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self'; object-src 'self'; plugin-types application/pdf;";
|
||||||
|
|
||||||
|
# The components of this path are originally double-URI-escaped
|
||||||
|
# (see zerver/view/upload.py). "location" matches are on
|
||||||
|
# unescaped values, which fills $s3_path with a properly
|
||||||
|
# single-escaped path to pass to the upstream server.
|
||||||
|
# (see associated commit message for more details)
|
||||||
|
set $download_url https://$s3_hostname/$s3_path;
|
||||||
|
proxy_set_header Host $s3_hostname;
|
||||||
|
proxy_ssl_name $s3_hostname;
|
||||||
|
proxy_ssl_server_name on;
|
||||||
|
|
||||||
|
# Strip off X-amz-cf-id header, which otherwise the request has to
|
||||||
|
# have been signed over, leading to signature mismatches.
|
||||||
|
proxy_set_header x-amz-cf-id "";
|
||||||
|
|
||||||
|
# Strip off any auth request headers which the Zulip client might
|
||||||
|
# have sent, as they will not work for S3, and will report an error due
|
||||||
|
# to the signed auth header we also provide.
|
||||||
|
proxy_set_header Authorization "";
|
||||||
|
proxy_set_header x-amz-security-token "";
|
||||||
|
|
||||||
|
# These headers are only valid if there is a body, but better to
|
||||||
|
# strip them to be safe.
|
||||||
|
proxy_set_header Content-Length "";
|
||||||
|
proxy_set_header Content-Type "";
|
||||||
|
proxy_set_header Content-MD5 "";
|
||||||
|
proxy_set_header x-amz-content-sha256 "";
|
||||||
|
proxy_set_header Expect "";
|
||||||
|
|
||||||
|
# Ensure that we only get _one_ of these response headers: the one
|
||||||
|
# that Django added, not the one from S3.
|
||||||
|
proxy_hide_header Cache-Control;
|
||||||
|
proxy_hide_header Expires;
|
||||||
|
proxy_hide_header Set-Cookie;
|
||||||
|
# We are _leaving_ S3 to provide Content-Type,
|
||||||
|
# Content-Disposition, and Accept-Ranges headers, which are the
|
||||||
|
# three remaining headers which nginx would also pass through from
|
||||||
|
# the first response. Django explicitly unsets the first, and
|
||||||
|
# does not set the latter two.
|
||||||
|
|
||||||
|
proxy_pass $download_url$is_args$args;
|
||||||
|
proxy_cache uploads;
|
||||||
|
# If the S3 response doesn't contain Cache-Control headers (which
|
||||||
|
# we don't expect it to) then we assume they are valid for a very
|
||||||
|
# long time. The size of the cache is controlled by
|
||||||
|
# `s3_disk_cache_size` and read frequency, set via
|
||||||
|
# `s3_cache_inactive_time`.
|
||||||
|
proxy_cache_valid 200 1y;
|
||||||
|
|
||||||
|
# We only include the requested content-disposition in the cache
|
||||||
|
# key, so that we cache "Content-Disposition: attachment"
|
||||||
|
# separately from the inline version.
|
||||||
|
proxy_cache_key $download_url$s3_disposition_cache_key;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"/internal/local/uploads/" = {
|
||||||
|
extraConfig = ''
|
||||||
|
internal;
|
||||||
|
include ${zulip-includes}/headers;
|
||||||
|
add_header Content-Security-Policy "default-src 'none'; media-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self'; object-src 'self'; plugin-types application/pdf;";
|
||||||
|
|
||||||
|
# Django handles setting Content-Type, Content-Disposition, and Cache-Control.
|
||||||
|
|
||||||
|
alias ${home}/uploads/files/;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"/internal/local/user_avatars/" = {
|
||||||
|
extraConfig = ''
|
||||||
|
internal;
|
||||||
|
include ${zulip-includes}/headers;
|
||||||
|
add_header Content-Security-Policy "default-src 'none' img-src 'self'";
|
||||||
|
include ${zulip-includes}/uploads.types;
|
||||||
|
alias ${home}/uploads/avatars/;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
# external_sso
|
||||||
|
"/accounts/login/sso/" = {
|
||||||
|
extraConfig = ''
|
||||||
|
proxy_pass https://localhost_sso;
|
||||||
|
include ${zulip-includes}/proxy;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
# camo
|
||||||
|
"/external_content/metrics" = {
|
||||||
|
extraConfig = ''
|
||||||
|
return 404;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
"/external_content/" = {
|
||||||
|
extraConfig = ''
|
||||||
|
rewrite /external_content/(.*) /$1 break;
|
||||||
|
proxy_pass http://camo;
|
||||||
|
include ${zulip-includes}/proxy;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
"/health/" = {
|
||||||
|
extraConfig = lib.concatStringsSep "\n" (
|
||||||
|
map (host: "accept ${host};") loadbalancers ++ [ "deny all;" ]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
27
machines/kat-son/zulip/postgres.nix
Normal file
27
machines/kat-son/zulip/postgres.nix
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
# postgresql_common.pp
|
||||||
|
users.users.postgres.extraGroups = [ "zulip" ];
|
||||||
|
# process_fts_updates.pp
|
||||||
|
systemd.services.process-fts-updates = {
|
||||||
|
script = ''''; # TODO : puppet/zulip/files/postgresql/process_fts_updates
|
||||||
|
serviceConfig = {
|
||||||
|
User = "zulip";
|
||||||
|
Group = "zulip";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
# postgresql_base.pp
|
||||||
|
services.nagios.plugins = [
|
||||||
|
# TODO : puppet/zulip/files/nagios_plugins/zulip_postgresql
|
||||||
|
];
|
||||||
|
services.postgresql = {
|
||||||
|
enable = true;
|
||||||
|
ensureUsers = [
|
||||||
|
{
|
||||||
|
name = "zulip";
|
||||||
|
ensureDBOwnership = true;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
ensureDatabases = [ "zulip" ];
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in a new issue