diff --git a/config/fudo/mail-container.nix b/config/fudo/mail-container.nix index 18ae663..5cae00e 100644 --- a/config/fudo/mail-container.nix +++ b/config/fudo/mail-container.nix @@ -1,5 +1,4 @@ -{ config, lib, ... }: - +{ lib, config, ... }: with lib; let cfg = config.fudo.mail-server; @@ -71,9 +70,12 @@ in rec { proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $host; ''; - trusted-network-string = optionalString ((length fudo-cfg.local-networks) > 0) + trusted-network-string = + optionalString ((length fudo-cfg.local-networks) > 0) (concatStringsSep "\n" - (map (network: "allow ${network};") fudo-cfg.local-networks)) + "\ndeny all;"; + (map (network: "allow ${network};") fudo-cfg.local-networks)) + '' + + deny all;''; in { "${cfg.hostname}" = { @@ -136,13 +138,9 @@ in rec { config = { config, pkgs, ... }: { - environment.systemPackages = with pkgs; [ - nmap - ]; + environment.systemPackages = with pkgs; [ nmap ]; - imports = [ - ./mail.nix - ]; + imports = [ ./mail.nix ]; environment = { etc = { diff --git a/fudo/users.nix b/fudo/users.nix index 9ee50d1..432aabb 100644 --- a/fudo/users.nix +++ b/fudo/users.nix @@ -432,4 +432,11 @@ common-name = "Network Info Mailer"; hashed-password = "{SSHA}UQHfW0IzjIbRU6VV+DraxvZFWt0to3oc"; }; + + selby-forum = { + uid = 10114; + group = "selby"; + common-name = "Selby Forum"; + hashed-password = "{SSHA}f7eDNuwFXRhvants5cJJ/FGtkCKheY2Q"; + }; } diff --git a/hosts/france.nix b/hosts/france.nix index 7c03bb1..00aba3d 100644 --- a/hosts/france.nix +++ b/hosts/france.nix @@ -43,7 +43,7 @@ in { nix-prefetch-docker powerdns tshark - # vanilla-forum + vanilla-forum ]; fudo.common = { @@ -269,7 +269,7 @@ in { fudo.mail-server = import ../fudo/email.nix { inherit config; } // { enableContainer = true; - debug = false; + debug = true; monitoring = true; hostname = mail-hostname; diff --git a/hosts/france/forum-config/config-defaults.php b/hosts/france/forum-config/config-defaults.php new file mode 100644 index 0000000..c078d59 --- /dev/null +++ b/hosts/france/forum-config/config-defaults.php @@ -0,0 +1,180 @@ + false, // PDO::ATTR_PERSISTENT + 1000 => true, // PDO::MYSQL_ATTR_USE_BUFFERED_QUERY (missing in some PHP installations) +]; + +// Use a dirty cache by default. Try Vanilla with memcached! +$Configuration['Cache']['Enabled'] = true; +$Configuration['Cache']['Method'] = 'dirtycache'; +$Configuration['Cache']['Filecache']['Store'] = PATH_CACHE.'/Filecache'; + +// Technical content stuff. +$Configuration['Garden']['ContentType'] = 'text/html'; +$Configuration['Garden']['Locale'] = 'en'; +$Configuration['Garden']['LocaleCodeset'] = 'UTF8'; + +$Configuration['HotReload']['IP'] = '127.0.0.1'; + +$Configuration['ContentSecurityPolicy']['ScriptSrc']['AllowedDomains'] = []; + +// Site specifics. +$Configuration['Garden']['Installed'] = false; // Has Garden been installed yet? This blocks setup when true. +$Configuration['Garden']['Title'] = 'Vanilla'; +$Configuration['Garden']['Domain'] = ''; +$Configuration['Garden']['WebRoot'] = false; // You can set this value if you are using htaccess to direct into the application, but the correct webroot isn't being recognized. +$Configuration['Garden']['StripWebRoot'] = false; +$Configuration['Garden']['AllowSSL'] = true; +$Configuration['Garden']['PrivateCommunity'] = false; +$Configuration['Garden']['Forms']['HoneypotName'] = 'hpt'; + +// Developer stuff. +$Configuration['Garden']['Debug'] = false; +$Configuration['Garden']['Errors']['LogFile'] = ''; +$Configuration['Garden']['FolderBlacklist'] = ['.', '..', '_svn', '.git']; // Folders we should never search for classes. + +// User registration & authentication. +$Configuration['Garden']['Session']['Length'] = '15 minutes'; +$Configuration['Garden']['Cookie']['Salt'] = ''; // We do this during setup, chill. +$Configuration['Garden']['Cookie']['Name'] = 'Vanilla'; +$Configuration['Garden']['Cookie']['Path'] = '/'; +$Configuration['Garden']['Cookie']['Domain'] = ''; +$Configuration['Garden']['Cookie']['HashMethod'] = 'md5'; // md5 or sha1 +$Configuration['Garden']['Authenticator']['DefaultScheme'] = 'password'; // Types include 'Password', 'Handshake', 'Openid' +$Configuration['Garden']['Authenticator']['RegisterUrl'] = '/entry/register?Target=%2$s'; +$Configuration['Garden']['Authenticator']['SignInUrl'] = '/entry/signin?Target=%2$s'; +$Configuration['Garden']['Authenticator']['SignOutUrl'] = '/entry/signout/{Session_TransientKey}?Target=%2$s'; +$Configuration['Garden']['Authenticator']['EnabledSchemes'] = ['password']; +$Configuration['Garden']['Authenticator']['SyncScreen'] = "smart"; +$Configuration['Garden']['Authenticators']['password']['Name'] = "Password"; +$Configuration['Garden']['UserAccount']['AllowEdit'] = true; // Allow users to edit their account information? (SSO requires accounts be edited in external system). +$Configuration['Garden']['Registration']['Method'] = 'Captcha'; // Options are: Basic, Captcha, Approval, Invitation +$Configuration['Garden']['Registration']['InviteExpiration'] = '1 week'; // When invitations expire. This will be plugged into strtotime(). +$Configuration['Garden']['Registration']['InviteRoles'] = 'FALSE'; +$Configuration['Garden']['Registration']['ConfirmEmail'] = false; +$Configuration['Garden']['Registration']['MinPasswordLength'] = 6; +$Configuration['Garden']['Registration']['NameUnique'] = true; +$Configuration['Garden']['TermsOfService'] = '/home/termsofservice'; // The url to the terms of service. +$Configuration['Garden']['Password']['MinLength'] = 6; +$Configuration['Garden']['Roles']['Manage'] = true; // @deprecated + +// Garden security features +$Configuration['Garden']['Security']['Hsts']['IncludeSubDomains'] = false; +$Configuration['Garden']['Security']['Hsts']['Preload'] = false; +$Configuration['Garden']['Security']['Hsts']['MaxAge'] = 604800; + +// Outgoing email. +$Configuration['Garden']['Email']['UseSmtp'] = false; +$Configuration['Garden']['Email']['SmtpHost'] = ''; +$Configuration['Garden']['Email']['SmtpUser'] = ''; +$Configuration['Garden']['Email']['SmtpPassword'] = ''; +$Configuration['Garden']['Email']['SmtpPort'] = '25'; +$Configuration['Garden']['Email']['SmtpSecurity'] = ''; // ssl/tls +$Configuration['Garden']['Email']['MimeType'] = 'text/plain'; +$Configuration['Garden']['Email']['SupportName'] = 'Support'; +$Configuration['Garden']['Email']['SupportAddress'] = ''; + +// Contact with the mothership. +$Configuration['Garden']['UpdateCheckUrl'] = 'https://open.vanillaforums.com/addons/update'; +$Configuration['Garden']['AddonUrl'] = 'https://open.vanillaforums.com/addons'; +$Configuration['Garden']['VanillaUrl'] = 'https://open.vanillaforums.com'; + +// File handling. +$Configuration['Garden']['CanProcessImages'] = false; +$Configuration['Garden']['Upload']['MaxFileSize'] = '50M'; +$Configuration['Garden']['Upload']['AllowedFileExtensions'] = [ + 'txt', 'jpg', 'jpeg', 'gif', 'png', 'bmp', 'tiff', 'ico', 'zip', 'gz', 'tar.gz', 'tgz', 'psd', 'ai', 'pdf', 'doc', 'xls', 'ppt', 'docx', 'xlsx', 'pptx', 'log', 'rar', '7z' +]; +$Configuration['Garden']['Profile']['MaxHeight'] = 560; +$Configuration['Garden']['Profile']['MaxWidth'] = 560; +$Configuration['Garden']['Thumbnail']['Size'] = 200; + +// Appearance. +$Configuration['Garden']['Theme'] = 'keystone'; +$Configuration['Garden']['MobileTheme'] = 'mobile'; +$Configuration['Garden']['Menu']['Sort'] = ['Dashboard', 'Discussions', 'Questions', 'Activity', 'Applicants', 'Conversations', 'User']; +$Configuration['Garden']['ThemeOptions']['Styles']['Key'] = 'Default'; +$Configuration['Garden']['ThemeOptions']['Styles']['Value'] = '%s_default'; + +// Profiles. +$Configuration['Garden']['Profile']['Public']= true; +$Configuration['Garden']['Profile']['ShowAbout'] = true; +$Configuration['Garden']['Profile']['EditPhotos'] = true; // false to disable user photo editing +$Configuration['Garden']['Profile']['EditUsernames'] = false; +$Configuration['Garden']['BannedPhoto'] = 'https://images.v-cdn.net/banned_large.png'; + +// Embedding forum & comments. +$Configuration['Garden']['Embed']['CommentsPerPage'] = 50; +$Configuration['Garden']['Embed']['SortComments'] = 'desc'; +$Configuration['Garden']['Embed']['PageToForum'] = true; +$Configuration['Garden']['SignIn']['Popup'] = true; // Should the sign-in link pop up or go to it's own page? (SSO requires going to it's own external page) + +// User experience & formatting. +$Configuration['Garden']['InputFormatter'] = 'Rich'; // Html, BBCode, Markdown, Text, Rich +$Configuration['Garden']['MobileInputFormatter'] = 'Rich'; +$Configuration['Garden']['Html']['AllowedElements'] = "a, abbr, acronym, address, area, audio, b, bdi, bdo, big, blockquote, br, caption, center, cite, code, col, colgroup, dd, del, details, dfn, div, dl, dt, em, figure, figcaption, font, h1, h2, h3, h4, h5, h6, hgroup, hr, i, img, ins, kbd, li, map, mark, menu, meter, ol, p, pre, q, s, samp, small, span, strike, strong, sub, sup, summary, table, tbody, td, tfoot, th, thead, time, tr, tt, u, ul, var, video, wbr"; +$Configuration['Garden']['Search']['Mode'] = 'boolean'; // matchboolean, match, boolean, like +$Configuration['Garden']['EditContentTimeout'] = 3600; // -1 means no timeout. 0 means immediate timeout. > 0 is in seconds. 60 * 60 = 3600 (aka 1hr) +$Configuration['Garden']['Format']['Mentions'] = true; +$Configuration['Garden']['Format']['Hashtags'] = false; +$Configuration['Garden']['Format']['YouTube'] = true; +$Configuration['Garden']['Format']['Vimeo'] = true; +$Configuration['Garden']['Format']['EmbedSize'] = 'normal'; // tiny/small/normal/big/huge or WIDTHxHEIGHT + +// Default preferences. Setting these to 'false' disables them globally. +$Configuration['Preferences']['Email']['ConversationMessage'] = '1'; +$Configuration['Preferences']['Email']['BookmarkComment'] = '1'; +$Configuration['Preferences']['Email']['ParticipateComment'] = '0'; +$Configuration['Preferences']['Email']['WallComment'] = '0'; +$Configuration['Preferences']['Email']['ActivityComment'] = '0'; +$Configuration['Preferences']['Email']['DiscussionComment'] = '0'; +$Configuration['Preferences']['Email']['Mention'] = '0'; +$Configuration['Preferences']['Popup']['ConversationMessage'] = '1'; +$Configuration['Preferences']['Popup']['BookmarkComment'] = '1'; +$Configuration['Preferences']['Popup']['ParticipateComment'] = '0'; +$Configuration['Preferences']['Popup']['WallComment'] = '1'; +$Configuration['Preferences']['Popup']['ActivityComment'] = '1'; +$Configuration['Preferences']['Popup']['DiscussionComment'] = '1'; +$Configuration['Preferences']['Popup']['Mention'] = '1'; + +// Module visibility and sorting. +$Configuration['Garden']['Modules']['ShowGuestModule'] = true; +$Configuration['Garden']['Modules']['ShowSignedInModule'] = false; +$Configuration['Garden']['Modules']['ShowRecentUserModule'] = false; +$Configuration['Modules']['Dashboard']['Panel'] = ['MeModule', 'UserBoxModule', 'ActivityFilterModule', 'UserPhotoModule', 'ProfileFilterModule', 'SideMenuModule', 'UserInfoModule', 'GuestModule', 'Ads']; +$Configuration['Modules']['Dashboard']['Content'] = ['MessageModule', 'MeModule', 'UserBoxModule', 'ProfileOptionsModule', 'Notices', 'ActivityFilterModule', 'ProfileFilterModule', 'Content', 'Ads']; +$Configuration['Modules']['Vanilla']['Panel'] = ['MeModule', 'UserBoxModule', 'GuestModule', 'NewDiscussionModule', 'DiscussionFilterModule', 'SignedInModule', 'Ads']; +$Configuration['Modules']['Vanilla']['Content'] = ['MessageModule', 'MeModule', 'UserBoxModule', 'NewDiscussionModule', 'ProfileOptionsModule', 'Notices', 'NewConversationModule', 'NewDiscussionModule', 'DiscussionFilterModule', 'CategoryModeratorsModule', 'Content', 'Ads']; +$Configuration['Modules']['Conversations']['Panel'] = ['MeModule', 'UserBoxModule', 'NewConversationModule', 'SignedInModule', 'GuestModule', 'Ads']; +$Configuration['Modules']['Conversations']['Content'] = ['MessageModule', 'MeModule', 'UserBoxModule', 'NewConversationModule', 'Notices', 'Content', 'Ads']; + +// Routes. +$Configuration['Routes']['DefaultController'] = 'discussions'; +$Configuration['Routes']['DefaultForumRoot'] = 'discussions'; +$Configuration['Routes']['Default404'] = ['dashboard/home/filenotfound', 'NotFound']; +$Configuration['Routes']['DefaultPermission'] = ['dashboard/home/unauthorized', 'NotAuthorized']; +$Configuration['Routes']['UpdateMode'] = 'dashboard/home/updatemode'; diff --git a/hosts/france/forum-config/config.php.nix b/hosts/france/forum-config/config.php.nix new file mode 100644 index 0000000..957c3af --- /dev/null +++ b/hosts/france/forum-config/config.php.nix @@ -0,0 +1,104 @@ +{ config }: + +'' + 0 is in seconds. 60 * 60 = 3600 (aka 1hr) + $Configuration["Garden"]["Format"]["Mentions"] = true; + $Configuration["Garden"]["Format"]["Hashtags"] = false; + $Configuration["Garden"]["Format"]["YouTube"] = true; + $Configuration["Garden"]["Format"]["Vimeo"] = true; + $Configuration["Garden"]["Format"]["EmbedSize"] = "normal"; // tiny/small/normal/big/huge or WIDTHxHEIGHT + + // Module visibility and sorting. + $Configuration["Garden"]["Modules"]["ShowGuestModule"] = true; + $Configuration["Garden"]["Modules"]["ShowSignedInModule"] = false; + $Configuration["Garden"]["Modules"]["ShowRecentUserModule"] = false; + $Configuration["Modules"]["Dashboard"]["Panel"] = ["MeModule", "UserBoxModule", "ActivityFilterModule", "UserPhotoModule", "ProfileFilterModule", "SideMenuModule", "UserInfoModule", "GuestModule", "Ads"]; + $Configuration["Modules"]["Dashboard"]["Content"] = ["MessageModule", "MeModule", "UserBoxModule", "ProfileOptionsModule", "Notices", "ActivityFilterModule", "ProfileFilterModule", "Content", "Ads"]; + $Configuration["Modules"]["Vanilla"]["Panel"] = ["MeModule", "UserBoxModule", "GuestModule", "NewDiscussionModule", "DiscussionFilterModule", "SignedInModule", "Ads"]; + $Configuration["Modules"]["Vanilla"]["Content"] = ["MessageModule", "MeModule", "UserBoxModule", "NewDiscussionModule", "ProfileOptionsModule", "Notices", "NewConversationModule", "NewDiscussionModule", "DiscussionFilterModule", "CategoryModeratorsModule", "Content", "Ads"]; + $Configuration["Modules"]["Conversations"]["Panel"] = ["MeModule", "UserBoxModule", "NewConversationModule", "SignedInModule", "GuestModule", "Ads"]; + $Configuration["Modules"]["Conversations"]["Content"] = ["MessageModule", "MeModule", "UserBoxModule", "NewConversationModule", "Notices", "Content", "Ads"]; + + // Routes. + $Configuration["Routes"]["DefaultController"] = "discussions"; + $Configuration["Routes"]["DefaultForumRoot"] = "discussions"; + $Configuration["Routes"]["Default404"] = ["dashboard/home/filenotfound", "NotFound"]; + $Configuration["Routes"]["DefaultPermission"] = ["dashboard/home/unauthorized", "NotAuthorized"]; + $Configuration["Routes"]["UpdateMode"] = "dashboard/home/updatemode"; + + // Cache. + $Configuration['Cache']['Enabled'] = true; + $Configuration['Cache']['Method'] = 'memcached'; + $Configuration['Cache']['Filecache']['Store'] = PATH_CACHE.'/Filecache'; + $Configuration['memcached']['Store'] = '${config.memcached-server}'; +'' diff --git a/hosts/france/forum-config/constants.php b/hosts/france/forum-config/constants.php new file mode 100644 index 0000000..b96801b --- /dev/null +++ b/hosts/france/forum-config/constants.php @@ -0,0 +1,89 @@ + + * @copyright 2009-2019 Vanilla Forums Inc. + * @license GPL-2.0-only + */ + + if (PHP_VERSION_ID < 70100) { + die("Vanilla requires PHP 7.1 or greater."); + } + + // Define the constants we need to get going. + if (!defined("APPLICATION")) { + define("APPLICATION", "Vanilla"); + } + if (!defined("APPLICATION_VERSION")) { + // Rules for the versioning + // {OSS version}-{Cloud release version}-{? SNAPSHOT if it"s a dev build} + define("APPLICATION_VERSION", "3.3"); + } + if (!defined("DS")) { + define("DS", DIRECTORY_SEPARATOR); + } + if (!defined("STATE_ROOT")) { + define("STATE_ROOT", "${state-root}"); + } + if (!defined("PATH_ROOT")) { + define("PATH_ROOT", "${static-root}"); + } + + // Disable Phar stream + stream_wrapper_unregister("phar"); + + /** + * Bootstrap Before + * + * This file gives developers the opportunity to hook into Garden before any + * real work has been done. Nothing has been included yet, aside from this file. + * No Garden features are available yet. + */ + $isWeb = PHP_SAPI !== "cli" && isset($_SERVER["REQUEST_METHOD"]); + if ($isWeb && file_exists(STATE_ROOT."/conf/bootstrap.before.php")) { + require_once STATE_ROOT."/conf/bootstrap.before.php"; + } + + /** + * Define Core Constants + * + * Garden depends on the presence of a certain base set of defines that allow it + * to be aware of its own place within the system. These are conditionally + * defined here, in case they"ve already been set by a zealous bootstrap.before. + */ + + // Path to the primary configuration file. + if (!defined("PATH_CONF")) { + define("PATH_CONF", "${config-root}"); + } + + // Include default constants. + require_once PATH_CONF."/constants.php"; + + // Make sure a default time zone is set. + // Do NOT edit this. See config `Garden.GuestTimeZone`. + date_default_timezone_set("UTC"); + + // Make sure the mb_* functions are utf8. + if (function_exists("mb_internal_encoding")) { + mb_internal_encoding("UTF-8"); + } + + // Include the core autoloader. + if (!include_once PATH_ROOT."/vendor/autoload.php") { + die("Could not find the autoloader. Did you forget to run 'composer install' in ".PATH_ROOT."?\n"); + } + spl_autoload_register([Vanilla\AliasLoader::class, "autoload"]); +'' diff --git a/hosts/france/forum-config/index.php b/hosts/france/forum-config/index.php new file mode 100644 index 0000000..f44e685 --- /dev/null +++ b/hosts/france/forum-config/index.php @@ -0,0 +1,29 @@ +start(); +$dispatcher->dispatch(); diff --git a/hosts/france/forum-config/index.php.nix b/hosts/france/forum-config/index.php.nix new file mode 100644 index 0000000..f018786 --- /dev/null +++ b/hosts/france/forum-config/index.php.nix @@ -0,0 +1,33 @@ +{ environment-file, bootstrap-file, ... }: + +'' + start(); + $dispatcher->dispatch(); +'' diff --git a/hosts/france/selby-forum-discourse-unused.nix b/hosts/france/selby-forum-discourse-unused.nix new file mode 100644 index 0000000..83cde12 --- /dev/null +++ b/hosts/france/selby-forum-discourse-unused.nix @@ -0,0 +1,119 @@ +{ config, lib, pkgs, ... }: + +let + hostname = "forum.test.selby.ca"; + local-port = "3157"; + + postgres-host = "france.fudo.org"; + config-path = "/srv/selby-forum/conf"; + redis-data-path = "/srv/selby-forum/redis-data"; + sidekiq-data-path = "/srv/selby-forum/sidekiq-data"; + discourse-data-path = "/srv/selby-forum/discourse-data"; + postgres-data-path = "/srv/selby-forum/postgres-data"; + + env-file = "/srv/selby-forum/private/env"; + + ensure-dir-and-ownership = ownership: dir: '' + if [ ! -d ${dir} ]; then + mkdir -p ${dir} + fi + + chown -R ${ownership} ${dir} + chmod 700 ${dir} + ''; + +in { + config = { + users.users = { + selby-discourse = { + isSystemUser = true; + # This is stupid: needs to be 1001, see bitnami docs + uid = 1001; + }; + }; + + security.acme.certs.${hostname}.email = "niten@fudo.org"; + + services.nginx = { + enable = true; + + virtualHosts = { + "${hostname}" = { + enableACME = true; + forceSSL = true; + + locations."/" = { + proxyPass = "http://127.0.0.1:${local-port}"; + + extraConfig = '' + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-By $server_addr:$server_port; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + ''; + }; + }; + }; + }; + + docker-containers = let + docker-flags = [ "--network=selby-discourse" "--env-file=${env-file}" ]; + discourse-env = { + DISCOURSE_USERNAME = "admin"; + DISCOURSE_SITENAME = "Selby Forum"; + DISCOURSE_EMAIL = "forum@selby.ca"; + DISCOURSE_HOSTNAME = hostname; + + POSTGRESQL_HOST = "selby-discourse-postgres"; + DISCOURSE_POSTGRESQL_USERNAME = "discourse_selby_forum"; + DISCOURSE_POSTGRESQL_NAME = "discourse_selby_forum"; + # note: passwords are stored in env-file + + REDIS_HOST = "selby-discourse-redis"; + # note: password is store in env-file + }; + + in { + selby-discourse = { + image = "bitnami/discourse:2.6.0"; + ports = [ "127.0.0.1:${local-port}:3000" ]; + # user = toString config.users.users.selby-discourse.uid; + volumes = [ + "${config-path}:/opt/bitnami/discourse/mounted-conf" + "${discourse-data-path}:/bitnami" + ]; + extraDockerOptions = docker-flags; + environment = discourse-env; + }; + + selby-discourse-redis = { + image = "bitnami/redis:6.0"; + user = toString config.users.users.selby-discourse.uid; + volumes = [ "${redis-data-path}:/bitnami/redis/data" ]; + extraDockerOptions = docker-flags; + environment = { }; + }; + + selby-discourse-sidekiq = { + image = "bitnami/discourse:2.6.0"; + # user = toString config.users.users.selby-discourse.uid; + volumes = [ "${sidekiq-data-path}:/bitnami" ]; + cmd = [ "nami" "start" "--foreground" "discourse-sidekiq" ]; + extraDockerOptions = docker-flags; + environment = discourse-env; + }; + + selby-discourse-postgres = { + image = "bitnami/postgresql:13"; + # user = toString config.users.users.selby-discourse.uid; + volumes = [ "${postgres-data-path}:/bitnami/postgresql" ]; + extraDockerOptions = docker-flags; + environment = { + POSTGRESQL_DATABASE = "discourse_selby_forum"; + POSTGRESQL_USERNAME = "discourse_selby_forum"; + }; + }; + }; + }; +} diff --git a/hosts/france/selby-forum.nix b/hosts/france/selby-forum.nix index 83cde12..422103e 100644 --- a/hosts/france/selby-forum.nix +++ b/hosts/france/selby-forum.nix @@ -2,116 +2,188 @@ let hostname = "forum.test.selby.ca"; - local-port = "3157"; - postgres-host = "france.fudo.org"; - config-path = "/srv/selby-forum/conf"; - redis-data-path = "/srv/selby-forum/redis-data"; - sidekiq-data-path = "/srv/selby-forum/sidekiq-data"; - discourse-data-path = "/srv/selby-forum/discourse-data"; - postgres-data-path = "/srv/selby-forum/postgres-data"; + mariadb-tag = "10"; + mariadb-port = "13306"; + mariadb-data-path = "/srv/selby-forum/mariadb-data"; + mariadb-root-env-file = "/srv/selby-forum/private/mariadb-env"; + mariadb-env-file = "/srv/selby-forum/private/mariadb-root-env"; + mariadb-username = "forum_selby_ca"; + mariadb-database = "forum_selby_ca"; - env-file = "/srv/selby-forum/private/env"; + mariadb-password-file = "/srv/selby-forum/private/mariadb-user-passwd"; - ensure-dir-and-ownership = ownership: dir: '' - if [ ! -d ${dir} ]; then - mkdir -p ${dir} - fi + smtp-password-file = "srv/selby-forum/private/smtp-passwd"; - chown -R ${ownership} ${dir} - chmod 700 ${dir} - ''; + fastcgi-params = "include ${pkgs.nginx}/conf/fastcgi_params"; + + memcached-tag = "1.6-alpine"; + memcached-port = "11219"; + + environment = pkgs.writeTextDir "/environment.php" + (import ./forum-config/environment.php.nix { + static-root = "${pkgs.vanilla-forum}"; + state-root = "/srv/selby-forum/state"; + config-root = "/etc/selby-forum"; + }); + + index = pkgs.writeTextDir "/index.php" (import ./forum-config/index.php.nix { + environment-file = "${environment}/environment.php"; + bootstrap-file = "${pkgs.vanilla-forum}/bootstrap.php"; + }); + + selby-forum-pkg = pkgs.symlinkJoin { + name = "selby-forum"; + paths = [ pkgs.vanilla-forum index ]; + }; in { config = { - users.users = { - selby-discourse = { - isSystemUser = true; - # This is stupid: needs to be 1001, see bitnami docs - uid = 1001; + environment.etc = { + "selby-forum/config-defaults.php" = { + uid = config.users.users.nginx.uid; + mode = "0400"; + source = ./forum-config/config-defaults.php; }; - }; - - security.acme.certs.${hostname}.email = "niten@fudo.org"; - - services.nginx = { - enable = true; - - virtualHosts = { - "${hostname}" = { - enableACME = true; - forceSSL = true; - - locations."/" = { - proxyPass = "http://127.0.0.1:${local-port}"; - - extraConfig = '' - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-By $server_addr:$server_port; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header X-Forwarded-Proto $scheme; - ''; + "selby-forum/constants.php" = { + uid = config.users.users.nginx.uid; + mode = "0400"; + source = ./forum-config/constants.php; + }; + "selby-forum/config.php" = { + uid = config.users.users.nginx.uid; + mode = "0400"; + text = import ./forum-config/config.php.nix { + config = { + database-host = "127.0.0.1:${mariadb-port}"; + database-name = mariadb-database; + database-user = mariadb-username; + database-password-file = mariadb-password-file; + site-name = "Selby Forum"; + site-domain = "forum.selby.ca"; + smtp-host = "mail.fudo.org"; + smtp-user = "selby-forum"; + smtp-password-file = smtp-password-file; + memcached-server = "127.0.0.1:${memcached-port}"; }; }; }; }; - docker-containers = let - docker-flags = [ "--network=selby-discourse" "--env-file=${env-file}" ]; - discourse-env = { - DISCOURSE_USERNAME = "admin"; - DISCOURSE_SITENAME = "Selby Forum"; - DISCOURSE_EMAIL = "forum@selby.ca"; - DISCOURSE_HOSTNAME = hostname; - - POSTGRESQL_HOST = "selby-discourse-postgres"; - DISCOURSE_POSTGRESQL_USERNAME = "discourse_selby_forum"; - DISCOURSE_POSTGRESQL_NAME = "discourse_selby_forum"; - # note: passwords are stored in env-file - - REDIS_HOST = "selby-discourse-redis"; - # note: password is store in env-file - }; - - in { - selby-discourse = { - image = "bitnami/discourse:2.6.0"; - ports = [ "127.0.0.1:${local-port}:3000" ]; - # user = toString config.users.users.selby-discourse.uid; - volumes = [ - "${config-path}:/opt/bitnami/discourse/mounted-conf" - "${discourse-data-path}:/bitnami" - ]; - extraDockerOptions = docker-flags; - environment = discourse-env; - }; - - selby-discourse-redis = { - image = "bitnami/redis:6.0"; - user = toString config.users.users.selby-discourse.uid; - volumes = [ "${redis-data-path}:/bitnami/redis/data" ]; - extraDockerOptions = docker-flags; - environment = { }; - }; - - selby-discourse-sidekiq = { - image = "bitnami/discourse:2.6.0"; - # user = toString config.users.users.selby-discourse.uid; - volumes = [ "${sidekiq-data-path}:/bitnami" ]; - cmd = [ "nami" "start" "--foreground" "discourse-sidekiq" ]; - extraDockerOptions = docker-flags; - environment = discourse-env; - }; - - selby-discourse-postgres = { - image = "bitnami/postgresql:13"; - # user = toString config.users.users.selby-discourse.uid; - volumes = [ "${postgres-data-path}:/bitnami/postgresql" ]; - extraDockerOptions = docker-flags; + docker-containers = { + selby-forum-mariadb = { + image = "mariadb:${mariadb-tag}"; + ports = [ "127.0.0.1:${mariadb-port}:3306" ]; + volumes = [ "${mariadb-data-path}:/var/lib/mysql" ]; environment = { - POSTGRESQL_DATABASE = "discourse_selby_forum"; - POSTGRESQL_USERNAME = "discourse_selby_forum"; + MYSQL_USER = mariadb-username; + MYSQL_DATABASE = mariadb-database; + }; + extraDockerOptions = [ + "--env-file=${mariadb-root-env-file}" + "--env-file=${mariadb-env-file}" + ]; + }; + selby-forum-memcached = { + image = "memcached:${memcached-tag}"; + ports = [ "127.0.0.1:${memcached-port}:11211" ]; + }; + }; + + security.acme.certs."${hostname}".email = "niten@fudo.org"; + + services = { + phpfpm = { + pools.selby-forum = { + user = "nginx"; + group = "nginx"; + + settings = { + "pm" = "dynamic"; + "pm.max_children" = 50; + "pm.start_servers" = 5; + "pm.min_spare_servers" = 1; + "pm.max_spare_servers" = 8; + }; + + phpOptions = '' + memory_limit = 500M + ''; + }; + }; + + nginx = { + enable = true; + + virtualHosts = { + "${hostname}" = let + forbidden-rxs = [ + "^.htaccess$" + "^/conf/" + "^/cache/" + "^/cgi-bin/" + "^/uploads/imports/" + "^/vendor/" + ]; + + forbidden-rx-entry = entry: + lib.nameValuePair "~* ${entry}" { + return = "403"; + extraConfig = "deny all;"; + }; + + forbidden-rx-entries = + builtins.listToAttrs (map forbidden-rx-entry forbidden-rxs); + + in { + enableACME = true; + forceSSL = true; + + root = "${selby-forum-pkg}/"; + + locations = forbidden-rx-entries // { + "/" = { + index = "index.php"; + tryFiles = "$uri @vanilla"; + }; + + "@vanilla" = { + extraConfig = '' + rewrite ^ /index.php$request_uri last; + ''; + }; + + "~* ^/index.php($|/)" = { + extraConfig = '' + expires -1; + + ${fastcgi-params}; + + fastcgi_split_path_info ^(.+?\.php)(/.*)$; + fastcgi_param SCRIPT_NAME /index.php; + fastcgi_param SCRIPT_FILENAME $realpath_root/index.php; + fastcgi_param X_REWRITE 1; + fastcgi_pass unix:${config.services.phpfpm.pools.selby-forum.socket}; + ''; + }; + }; + }; + }; + }; + }; + + systemd.services = { + phpfpm-selby-forum-socket-perm = { + wantedBy = [ "multi-user.target" "nginx.service" ]; + before = [ "nginx.service" ]; + description = + "Change ownership of the phpfpm socket for selby forum once it's started."; + requires = [ "phpfpm-selby-forum.service" ]; + after = [ "phpfpm.target" "phpfpm-selby-forum.service" ]; + serviceConfig = { + ExecStart = '' + ${pkgs.coreutils}/bin/chown nginx:nginx ${config.services.phpfpm.pools.selby-forum.socket} + ''; }; }; }; diff --git a/packages/local.nix b/packages/local.nix index 43ce5de..b61b741 100644 --- a/packages/local.nix +++ b/packages/local.nix @@ -158,10 +158,10 @@ in { doom-emacs-config = pkgs.fetchgit { url = "https://git.fudo.org/niten/doom-emacs.git"; - rev = "f89cc7e24e09f0ea24ce7eef8aaf4f061744094c"; - sha256 = "1cwf1dfknnpkg1gpj2c0nihxw00n790xsfli4fk3iqm62m66cw1s"; + rev = "ecf6d3e1701dfbf984cc5842df564c53f59c73fc"; + sha256 = "0n4hwp2pdr9dgcm9fsq98ql3k834vf876p1bl98cnd98aj9my1b6"; }; - # vanilla-forum = import vanilla-forum.nix { inherit pkgs lib; }; + vanilla-forum = import ./vanilla-forum.nix { pkgs = pkgs; }; }; } diff --git a/packages/vanilla-forum.nix b/packages/vanilla-forum.nix new file mode 100644 index 0000000..302bb19 --- /dev/null +++ b/packages/vanilla-forum.nix @@ -0,0 +1,33 @@ +# NOT USED, CAN DELETE + +{ pkgs, ... }: + +let version = "3.3"; + +in pkgs.stdenv.mkDerivation { + pname = "vanilla-forum"; + version = version; + + src = builtins.fetchurl { + name = "vanilla-forum-${version}.zip"; + url = "https://us.v-cdn.net/5018160/uploads/addons/3JQXC5NIGUWR.zip"; + sha256 = "13062ar0mdaaihzj6jx9kjvfvsg3km8khvad1rm9cqxviim9rzv3"; + }; + + nativeBuildInputs = with pkgs; [ unzip ]; + + # unpackPhase = '' + # ${pkgs.unzip}/bin/unzip $src + # ''; + + installPhase = '' + mkdir $out + cp -aR -t $out applications bootstrap.php dist js library locales plugins resources themes uploads vendor + ''; + + meta = { + homepage = "http://vanillaforums.com/"; + description = "Vanilla Web Forum"; + downloadPage = "https://open.vanillaforums.com/download"; + }; +}