421 lines
13 KiB
Nix
421 lines
13 KiB
Nix
# Copyright Tom Hubrecht, (2023)
|
|
#
|
|
# Tom Hubrecht <tom@hubrecht.ovh>
|
|
#
|
|
# This software is a computer program whose purpose is to configure
|
|
# machines and servers with NixOS.
|
|
#
|
|
# This software is governed by the CeCILL license under French law and
|
|
# abiding by the rules of distribution of free software. You can use,
|
|
# modify and/ or redistribute the software under the terms of the CeCILL
|
|
# license as circulated by CEA, CNRS and INRIA at the following URL
|
|
# "http://www.cecill.info".
|
|
#
|
|
# As a counterpart to the access to the source code and rights to copy,
|
|
# modify and redistribute granted by the license, users are provided only
|
|
# with a limited warranty and the software's author, the holder of the
|
|
# economic rights, and the successive licensors have only limited
|
|
# liability.
|
|
#
|
|
# In this respect, the user's attention is drawn to the risks associated
|
|
# with loading, using, modifying and/or developing or reproducing the
|
|
# software by the user in light of its specific status of free software,
|
|
# that may mean that it is complicated to manipulate, and that also
|
|
# therefore means that it is reserved for developers and experienced
|
|
# professionals having in-depth computer knowledge. Users are therefore
|
|
# encouraged to load and test the software's suitability as regards their
|
|
# requirements in conditions enabling the security of their systems and/or
|
|
# data to be ensured and, more generally, to use and operate it in the
|
|
# same conditions as regards security.
|
|
#
|
|
# The fact that you are presently reading this means that you have had
|
|
# knowledge of the CeCILL license and that you accept its terms.
|
|
|
|
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
|
|
let
|
|
inherit (lib)
|
|
mdDoc
|
|
mkDefault
|
|
mkEnableOption
|
|
mkIf
|
|
mkOption
|
|
|
|
optional
|
|
optionalString
|
|
|
|
types
|
|
;
|
|
|
|
cfg = config.services.demarches-simplifiees;
|
|
|
|
settingsFormat = pkgs.formats.keyValue { };
|
|
|
|
env = settingsFormat.generate "ds-fr-env" cfg.settings;
|
|
|
|
ds-fr = pkgs.writeShellScriptBin "ds-fr" ''
|
|
set -a
|
|
cd ${cfg.package}
|
|
|
|
${optionalString (cfg.secretFile != null) "source ${cfg.secretFile}"}
|
|
source ${env}
|
|
|
|
BIN="$1"
|
|
shift
|
|
|
|
SUDO="exec"
|
|
if [[ $USER != ${cfg.user} ]]; then
|
|
SUDO='exec /run/wrappers/bin/sudo -u ${cfg.user} --preserve-env'
|
|
fi
|
|
|
|
$SUDO ${cfg.package}/bin/$BIN "$@"
|
|
'';
|
|
in
|
|
{
|
|
options.services.demarches-simplifiees = {
|
|
enable = mkEnableOption "demarches-simplifiees.";
|
|
|
|
package = mkOption {
|
|
type = types.package;
|
|
default = pkgs.callPackage ./package { inherit (cfg) initialDeploymentDate dataDir logDir; };
|
|
};
|
|
|
|
user = mkOption {
|
|
type = types.str;
|
|
default = "ds-fr";
|
|
description = mdDoc "User account under which DS runs.";
|
|
};
|
|
|
|
group = mkOption {
|
|
type = types.str;
|
|
default = "ds-fr";
|
|
description = mdDoc "Group account under which DS runs.";
|
|
};
|
|
|
|
dataDir = mkOption {
|
|
type = types.str;
|
|
default = "/var/lib/ds-fr";
|
|
};
|
|
|
|
logDir = mkOption {
|
|
type = types.str;
|
|
default = "/var/log/ds-fr";
|
|
};
|
|
|
|
secretFile = mkOption {
|
|
type = types.nullOr types.path;
|
|
default = null;
|
|
};
|
|
|
|
settings = mkOption { inherit (settingsFormat) type; };
|
|
|
|
initialDeploymentDate = mkOption {
|
|
type = types.nullOr types.str;
|
|
default = null;
|
|
};
|
|
};
|
|
|
|
config = mkIf cfg.enable {
|
|
|
|
environment.systemPackages = [ ds-fr ];
|
|
|
|
systemd.tmpfiles.rules = [
|
|
"f '${cfg.logDir}/production.log' 0640 ${cfg.user} ${cfg.group} - -"
|
|
"f '${cfg.dataDir}/.env' 0600 ${cfg.user} ${cfg.group} - -"
|
|
"d '${cfg.dataDir}/tmp' 0700 ${cfg.user} ${cfg.group} 10d -"
|
|
"d '${cfg.dataDir}/storage' 0700 ${cfg.user} ${cfg.group} - -"
|
|
];
|
|
|
|
systemd.services = {
|
|
ds-fr-setup = {
|
|
description = "Demarches Simplifiees setup";
|
|
|
|
wantedBy = [ "multi-user.target" ];
|
|
path = [
|
|
pkgs.bash
|
|
ds-fr
|
|
];
|
|
after = [ "postgresql.service" ];
|
|
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
User = cfg.user;
|
|
Group = cfg.group;
|
|
EnvironmentFile = [ env ] ++ (optional (cfg.secretFile != null) cfg.secretFile);
|
|
StateDirectory = mkIf (cfg.dataDir == "/var/lib/ds-fr") "ds-fr";
|
|
LogsDirectory = mkIf (cfg.logDir == "/var/log/ds-fr") "ds-fr";
|
|
};
|
|
|
|
script = ''
|
|
[[ ! -f ${cfg.dataDir}/.initial-migration ]] \
|
|
&& ds-fr rails db:environment:set \
|
|
&& ds-fr rails db:schema:load \
|
|
&& ds-fr rails db:seed \
|
|
&& touch ${cfg.dataDir}/.initial-migration
|
|
|
|
ds-fr rake db:migrate
|
|
ds-fr rake after_party:run
|
|
'';
|
|
};
|
|
|
|
ds-fr-work = {
|
|
description = "Demarches Simplifiees work service";
|
|
|
|
wantedBy = [
|
|
"multi-user.target"
|
|
"ds-fr.service"
|
|
];
|
|
after = [
|
|
"network.target"
|
|
"ds-fr-setup.service"
|
|
];
|
|
requires = [ "ds-fr-setup.service" ];
|
|
|
|
serviceConfig = {
|
|
ExecStart = "${ds-fr}/bin/ds-fr rails jobs:work";
|
|
EnvironmentFile = [ env ] ++ (optional (cfg.secretFile != null) cfg.secretFile);
|
|
User = cfg.user;
|
|
Group = cfg.group;
|
|
StateDirectory = mkIf (cfg.dataDir == "/var/lib/ds-fr") "ds-fr";
|
|
LogsDirectory = mkIf (cfg.logDir == "/var/log/ds-fr") "ds-fr";
|
|
};
|
|
};
|
|
|
|
ds-fr = {
|
|
description = "Demarches Simplifiees web service";
|
|
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = [
|
|
"network.target"
|
|
"ds-fr-setup.service"
|
|
];
|
|
requires = [ "ds-fr-setup.service" ];
|
|
path = [ pkgs.imagemagick ];
|
|
|
|
serviceConfig = {
|
|
ExecStart = "${ds-fr}/bin/ds-fr rails server";
|
|
Environment = [ "RAILS_QUEUE_ADAPTER=delayed_job" ];
|
|
EnvironmentFile = [ env ] ++ (optional (cfg.secretFile != null) cfg.secretFile);
|
|
User = cfg.user;
|
|
Group = cfg.group;
|
|
StateDirectory = mkIf (cfg.dataDir == "/var/lib/ds-fr") "ds-fr";
|
|
LogsDirectory = mkIf (cfg.logDir == "/var/log/ds-fr") "ds-fr";
|
|
};
|
|
};
|
|
};
|
|
|
|
services = {
|
|
demarches-simplifiees.settings =
|
|
(builtins.mapAttrs (_: mkDefault) {
|
|
RAILS_ENV = "production";
|
|
RAILS_ROOT = builtins.toString cfg.package;
|
|
|
|
# Application host name
|
|
#
|
|
# Examples:
|
|
# * For local development: localhost:3000
|
|
# * For preproduction: staging.ds.example.org
|
|
# * For production: ds.example.org
|
|
APP_HOST = "localhost:3000";
|
|
|
|
# Rails key for signing sensitive data
|
|
# See https://guides.rubyonrails.org/security.html
|
|
#
|
|
# For production you MUST generate a new key, and keep it secret.
|
|
# Secrets must be long and random. Use bin/rails secret to get new unique secrets.
|
|
|
|
# Secret key for One-Time-Password codes, used for 2-factors authentication
|
|
# OTP_SECRET_KEY = "";
|
|
|
|
# Protect access to the instance with a static login/password (useful for staging environments)
|
|
BASIC_AUTH_ENABLED = "disabled";
|
|
BASIC_AUTH_USERNAME = "";
|
|
BASIC_AUTH_PASSWORD = "";
|
|
|
|
# ActiveStorage service to use for attached files.
|
|
# Possible values:
|
|
# - "local": store files on the local filesystem
|
|
# - "amazon": store files remotely on an S3 storage service
|
|
# - "openstack": store files remotely on an OpenStack storage service
|
|
#
|
|
# (See config/storage.yml for the configuration of each service.)
|
|
ACTIVE_STORAGE_SERVICE = "local";
|
|
|
|
# Configuration for the OpenStack storage service (if enabled)
|
|
FOG_OPENSTACK_API_KEY = "";
|
|
FOG_OPENSTACK_USERNAME = "";
|
|
FOG_OPENSTACK_URL = "";
|
|
FOG_OPENSTACK_REGION = "";
|
|
DS_PROXY_URL = "";
|
|
|
|
# SAML
|
|
SAML_IDP_ENABLED = "disabled";
|
|
|
|
# External service: authentication through France Connect
|
|
FC_PARTICULIER_ID = "";
|
|
FC_PARTICULIER_SECRET = "";
|
|
FC_PARTICULIER_BASE_URL = "";
|
|
|
|
# External service: authentication through Agent Connect
|
|
AGENT_CONNECT_ID = "";
|
|
AGENT_CONNECT_SECRET = "";
|
|
AGENT_CONNECT_BASE_URL = "";
|
|
AGENT_CONNECT_JWKS = "";
|
|
AGENT_CONNECT_REDIRECT = "";
|
|
|
|
# External service: integration with HelpScout (optional)
|
|
HELPSCOUT_MAILBOX_ID = "";
|
|
HELPSCOUT_CLIENT_ID = "";
|
|
HELPSCOUT_CLIENT_SECRET = "";
|
|
HELPSCOUT_WEBHOOK_SECRET = "";
|
|
|
|
# External service: external supervision
|
|
SENTRY_ENABLED = "disabled";
|
|
SENTRY_CURRENT_ENV = "development";
|
|
SENTRY_DSN_RAILS = "";
|
|
SENTRY_DSN_JS = "";
|
|
|
|
# External service: Matomo web analytics
|
|
MATOMO_ENABLED = "disabled";
|
|
MATOMO_COOKIE_DOMAIN = "*.www.demarches-simplifiees.fr";
|
|
MATOMO_DOMAIN = "*.www.demarches-simplifiees.fr";
|
|
MATOMO_ID = "";
|
|
MATOMO_HOST = "matomo.example.org";
|
|
|
|
# Default SMTP Provider: Mailjet
|
|
MAILJET_API_KEY = "";
|
|
MAILJET_SECRET_KEY = "";
|
|
|
|
# Alternate SMTP Provider: SendInBlue/DoList
|
|
SENDINBLUE_CLIENT_KEY = "";
|
|
SENDINBLUE_SMTP_KEY = "";
|
|
SENDINBLUE_USER_NAME = "";
|
|
# SENDINBLUE_LOGIN_URL="https://app.sendinblue.com/account/saml/login/truc"
|
|
|
|
# Alternate SMTP Provider: Mailtrap (mail catcher for staging environments)
|
|
# When enabled, all emails will be sent using this provider
|
|
MAILTRAP_ENABLED = "disabled";
|
|
MAILTRAP_USERNAME = "";
|
|
MAILTRAP_PASSWORD = "";
|
|
|
|
# Alternative SMTP Provider: Mailcatcher (Catches mail and serves it through a dream.)
|
|
# When enabled, all emails will be sent using this provider
|
|
MAILCATCHER_ENABLED = "disabled";
|
|
MAILCATCHER_HOST = "";
|
|
MAILCATCHER_PORT = "";
|
|
|
|
# External service: live chat for admins (specific to démarches-simplifiées.fr)
|
|
CRISP_ENABLED = "disabled";
|
|
CRISP_CLIENT_KEY = "";
|
|
|
|
# API Entreprise credentials
|
|
# https://api.gouv.fr/api/api-entreprise.html
|
|
API_ENTREPRISE_KEY = "";
|
|
|
|
# External service: CRM for following admin accounts pipeline (specific to démarches-simplifiées.fr)
|
|
PIPEDRIVE_KEY = "";
|
|
|
|
# Networks bypassing the email login token that verifies new devices, and rack-attack throttling
|
|
TRUSTED_NETWORKS = "";
|
|
|
|
# External service: mesuring performance of the Rails app (specific to démarches-simplifiées.fr)
|
|
SKYLIGHT_AUTHENTICATION_KEY = "";
|
|
# "sXaot-fKhBlkI8qaSirQyuZbrpv5sVFoOturQ0pFEh0";
|
|
|
|
# Enable or disable Lograge logs
|
|
LOGRAGE_ENABLED = "disabled";
|
|
|
|
# Logs source for Lograge
|
|
#
|
|
# Examples:
|
|
# * For local development: tps_local
|
|
# * For preproduction: tps_staging
|
|
# * For production: tps_prod
|
|
LOGRAGE_SOURCE = "tps_prod";
|
|
|
|
# External service: timestamping a daily archive of dossiers status changes
|
|
UNIVERSIGN_API_URL = "https://ws.universign.eu/tsa/post/";
|
|
UNIVERSIGN_USERPWD = "";
|
|
|
|
# External service: API Geo / Adresse
|
|
API_ADRESSE_URL = "https://api-adresse.data.gouv.fr";
|
|
API_GEO_URL = "https://geo.api.gouv.fr";
|
|
|
|
# External service: API Education
|
|
API_EDUCATION_URL = "https://data.education.gouv.fr/api/records/1.0";
|
|
|
|
# Encryption key for sensitive columns in the database
|
|
ENCRYPTION_SERVICE_SALT = "";
|
|
|
|
# ActiveRecord encryption keys. Generate them with bin/rails db:encryption:init (you can omit deterministic_key)
|
|
AR_ENCRYPTION_PRIMARY_KEY = "";
|
|
AR_ENCRYPTION_KEY_DERIVATION_SALT = "";
|
|
|
|
# Salt for invisible_captcha session data.
|
|
# Must be the same value for all app instances behind a load-balancer.
|
|
INVISIBLE_CAPTCHA_SECRET = "kikooloool";
|
|
|
|
# Clamav antivirus usage
|
|
CLAMAV_ENABLED = "disabled";
|
|
|
|
# Siret number used for API Entreprise, by default we use SIRET from dinum
|
|
API_ENTREPRISE_DEFAULT_SIRET = "put_your_own_siret";
|
|
})
|
|
// {
|
|
# Database credentials
|
|
DB_DATABASE = "ds-fr";
|
|
DB_USERNAME = cfg.user;
|
|
DB_PASSWORD = "";
|
|
DB_HOST = "/run/postgresql";
|
|
DB_POOL = "";
|
|
|
|
# Log on stdout
|
|
RAILS_LOG_TO_STDOUT = true;
|
|
};
|
|
|
|
postgresql = {
|
|
enable = true;
|
|
|
|
ensureDatabases = [ "ds-fr" ];
|
|
|
|
ensureUsers = optional (cfg.user == "ds-fr") {
|
|
name = "ds-fr";
|
|
ensureDBOwnership = true;
|
|
};
|
|
|
|
extraPlugins = with config.services.postgresql.package.pkgs; [ postgis ];
|
|
};
|
|
|
|
nginx = {
|
|
enable = true;
|
|
|
|
virtualHosts.${cfg.settings.APP_HOST} = {
|
|
enableACME = true;
|
|
forceSSL = true;
|
|
root = "${cfg.package}/public/";
|
|
|
|
locations."/".tryFiles = "$uri @proxy";
|
|
locations."@proxy" = {
|
|
proxyPass = "http://127.0.0.1:3000";
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
users.users = mkIf (cfg.user == "ds-fr") {
|
|
ds-fr = {
|
|
inherit (cfg) group;
|
|
|
|
isSystemUser = true;
|
|
home = cfg.package;
|
|
};
|
|
};
|
|
|
|
users.groups.${cfg.group} = { };
|
|
};
|
|
}
|