Compare commits

..

120 commits

Author SHA1 Message Date
ab6db28ada add Adrien@Lille to ifconfig 2023-03-15 18:19:58 +01:00
46e29828b1 add missing iptables rules 2023-02-12 16:40:14 +01:00
a6742bcf53
fix io 2023-02-02 07:45:38 +01:00
653e170fb2
remove outdated info 2022-12-24 23:00:33 +01:00
b449e83870
Notice that repo is obsolete 2022-12-22 17:59:51 +01:00
b575b2b486
Remove all files from op_guide, now migrated to guide.deuxfleurs.fr 2022-12-22 17:46:19 +01:00
015c372532
Add allowed ipv6 prefix 2022-09-09 17:25:34 +02:00
ec597541c8
Fix create db doc 2022-08-25 02:02:40 +02:00
ed82071223
Upgrade Stolon doc 2022-08-24 17:09:40 +02:00
18610f9a9a
Add Quentin@Lyon (orion) to iptables v6 rules 2022-08-24 16:29:02 +02:00
11a2ffa89d
Upgrade Stolon to Posgtgres 14 2022-08-24 15:58:21 +02:00
ae91f66fac
Disable guichet on old cluster 2022-08-24 15:51:29 +02:00
145f3a8499
Matrix is so weird... 2022-08-19 18:27:43 +02:00
638f775742
Hot fix 2022-08-19 18:01:19 +02:00
38a0feffe0
Add zorun 2022-08-18 22:31:34 +02:00
1e003461bd
Add the net target to io 2022-08-17 12:26:23 +02:00
2e872eb87f
Update max@bruxelles IP addresses 2022-08-17 11:50:48 +02:00
ef265b87de
Update doc 2022-07-28 17:34:49 +02:00
64172fc999
update runners' doc 2022-07-25 15:20:21 +02:00
ceae80d87c
Use Tricot certificates instead of self-signed ones 2022-07-06 13:16:50 +02:00
0e81c9f23b
Upgrade Matrix 2022-07-01 14:17:33 +02:00
39e3ecce64
Upgrade Synapse + Element Web 2022-07-01 13:59:50 +02:00
51482e16e4
Drop allow unsafe locale 2022-06-06 10:52:18 +02:00
6c31560c7b
Forced to allow unsafe local 2022-06-06 09:08:51 +02:00
72b41408ef
Upgrade synapse+element web in Nomad 2022-06-06 09:03:51 +02:00
7dd2aeb63b
Upgrade matrix+riot 2022-06-06 08:42:57 +02:00
a17640d606
update bottin config 2022-06-01 12:41:38 +02:00
241dd1e175
Drone update 2022-05-31 11:53:42 +02:00
d712c08dbc
Update the doc 2022-05-10 15:42:41 +02:00
415075b010
Garage v0.7.1 2022-05-09 16:25:15 +02:00
2021b7d08c
New ipv6 prefix for lx@orsay 2022-05-09 00:10:21 +02:00
99a4f51166
Simplify the build 2022-05-06 10:49:28 +02:00
653e45f192
Packaging try on Cryptpad 2022-05-06 10:32:41 +02:00
f0ead6efed
WIP Cryptpad packaging 2022-05-05 17:45:15 +02:00
f27636dd14
Add headers in Garage 2022-05-05 08:50:33 +02:00
d7164c7d90
remove obsolete admin_port 2022-05-04 17:33:43 +02:00
5b861cd652
Remove unused Traefik config 2022-05-04 17:28:39 +02:00
79d68c4aa3
Update tricot 2022-05-04 17:27:54 +02:00
4cb1dbe663
Add a security HTTPS header to Garage web 2022-05-04 09:20:07 +02:00
d21c010da1
Set plume log verbosity to info 2022-04-24 13:45:32 +02:00
60ad398c44
Upgrade Plume + debug info 2022-04-23 22:04:14 +02:00
2695a79e8a
Add garage backup info 2022-04-23 13:27:52 +02:00
1e9a538be9
add concrete examples 2022-04-19 14:41:03 +02:00
c69923f104
Add missing doc 2022-04-19 14:38:29 +02:00
d62f87fa71
Update guide 2022-04-19 14:32:44 +02:00
501fbb5553
Add doc for secrets 2022-04-19 13:46:12 +02:00
b2b26879cb replace os.system with subprocess.run 2022-04-15 14:57:54 +02:00
83745f737a Deployment on Nomad 2022-04-15 14:24:41 +02:00
8cf1b0c3e4 Build image via Nix 2022-04-15 12:36:49 +02:00
9701b863fd Create a backup script 2022-04-14 17:50:17 +02:00
1183583fdf
make adrien admin 2022-04-06 12:17:15 +02:00
1e5e4af35c Ajout de Publii dans le postmortem 2022-03-30 10:04:54 +02:00
ce36e7e09b Ajout coupure élec + SSD lent 2022-03-28 11:59:37 +02:00
68607d567c Ajout de matrix 2022-03-28 11:55:25 +02:00
b5137f6665 Ajout de GlusterFS 2022-03-28 11:51:49 +02:00
3f73721ad5
documentation de petits incidents techniques plus ou moins évitables 2022-03-28 11:43:47 +02:00
0e6aa95754
Update Garage to 0.7.0-rc1 2022-03-28 10:59:24 +02:00
306974a163 Change Plume restart policy 2022-03-18 11:37:14 +01:00
9883d85c2a Small postfix modifications 2022-03-14 10:02:22 +01:00
a1c6c33d73 Maintenance du 2022-03-09 2022-03-09 16:54:19 +01:00
1322dae8da Upgrade Matrix 2022-03-09 11:52:36 +01:00
e7329a0202 Add zstd 2022-03-09 11:32:43 +01:00
b359601d2d Documentation for Drone 2022-03-07 11:02:37 +01:00
8ce62ddca1
Close drone registrations 2022-02-21 14:54:42 +01:00
0b16fd1c08
Update Garage and change a few config parameters 2022-02-10 14:34:18 +01:00
41e1a31bb9
fix typo 2022-02-09 16:06:23 +01:00
1410f2f8d8
Add LX@Orsay to trusted net 2022-02-09 15:53:45 +01:00
f74651a0c3
Upgrade garage to 0.6 RC1 2022-02-01 15:33:33 +01:00
5ecab67379 Use a list to organize ref 2022-01-28 19:14:39 +01:00
f3dbf47547 Ajout de pg_verifybackup 2022-01-28 19:11:58 +01:00
37bea48d45 Finalize manual backup 2022-01-28 18:44:07 +01:00
89937f2107 Update guide 2022-01-28 17:00:50 +01:00
2775eeb0fe WIP manual backup 2022-01-27 18:26:02 +01:00
715c3d3a9f Use ampersand in backup instead of semi colon 2022-01-27 16:58:22 +01:00
84b26f347d Add consul backup with restic 2022-01-27 16:56:02 +01:00
3baa511fce Plume backup + WIP consul 2022-01-27 16:32:57 +01:00
00d7106a18 Redeploy plume 2022-01-27 13:31:25 +01:00
831ddd3055 Some fixes 2022-01-27 09:57:49 +01:00
a13a02c45c Add a backup script for emails 2022-01-26 21:48:48 +01:00
453b633268 Update guide 2022-01-26 19:31:44 +01:00
a68a1e1da7 Migrate jitsi + WIP backup doc 2022-01-26 19:09:26 +01:00
3563fb5994 Change how email is stored 2022-01-26 17:20:20 +01:00
7cede37e6d Mises à jour du cluster 2022-01-25 12:12:58 +01:00
f229d58467
Update tricot and increase RAM allocation 2022-01-11 15:07:33 +01:00
87986ff3cf
Move out .hcl files specific to Neptune cluster 2021-12-25 19:40:30 +01:00
85eb4d5b82
Revert garage to 0.5.0 temporarily to fix winscp bug 2021-12-15 11:18:04 +01:00
59ce079a52
Update tricot 2021-12-14 11:43:18 +01:00
582882286e
latest s3 provider version is required 2021-12-14 11:19:09 +01:00
fa75e0012c
Also upgrade async upload 2021-12-14 11:12:40 +01:00
e9ba2243e7
Update Matrix 2021-12-14 11:05:41 +01:00
3df786a5f5
Don't use ipv6 in garage staging cluster 2021-12-13 11:44:27 +01:00
50a09980c5 Update jitsi's nomad service 2021-12-12 13:21:49 +01:00
f73d8dab93 log4shell mitigation 2021-12-12 13:03:45 +01:00
c00f0fefe7 Update bagage 2021-12-12 12:49:48 +01:00
2fc9276be2
fixed tricot with compression now 2021-12-10 00:26:51 +01:00
c6819c8d4a
Revert for now 2021-12-09 16:52:16 +01:00
d64fe28143
upgrade tricot to enable compression 2021-12-09 16:14:17 +01:00
783894b60d
Tricot 19 2021-12-09 12:24:18 +01:00
854da5b984
Different tricot config for neptune dc 2021-12-09 11:04:56 +01:00
8d178815d6
Only one frontend 2021-12-09 10:51:58 +01:00
2d2e7bb5c6
fix tricot 2021-12-08 23:48:08 +01:00
ea55c9b12b
synapse on dummy infrastructure for tricot test 2021-12-08 18:05:17 +01:00
3693d9f36b
Traefik on all servers 2021-12-08 13:32:47 +01:00
a4982c6cd6
last tricot version 2021-12-08 13:28:22 +01:00
7f08d5f324
Add tricot tags to everything 2021-12-08 12:42:48 +01:00
2c2ee6c903
Rename tricot+traefik to frontend 2021-12-08 12:21:50 +01:00
3297135a58
Add tricot to replace traefik 2021-12-08 12:19:08 +01:00
8846421cc4
Deploy core on neptune as well 2021-12-08 11:41:07 +01:00
fff6f1db20
garage with new s3_router 2021-12-06 22:10:26 +01:00
ef2fa848f1
single region staging cluster 2021-12-04 21:56:15 +01:00
4cc6a0182c
Bump synapse to 1.47.1 to fix CVE 2021-11-23 13:48:12 +01:00
7113a3ae56 Add secrets 2021-11-20 14:58:09 +01:00
5df7058c84 Working SFTP deployment of Garage 2021-11-20 14:56:56 +01:00
9ce6c7ad6e
Add config files for garage staging cluster 2021-11-18 17:14:30 +01:00
0268f63f66
Upgrade garage to 0.5 2021-11-17 16:42:13 +01:00
948a916c2f
Add missing options for discord bridge 2021-11-16 12:57:15 +01:00
289359cedc
Prepare to add Discord bridge 2021-11-16 12:05:28 +01:00
627c89b545
make config file clearer 2021-11-15 23:05:01 +01:00
e20b903bc0
Add matterbridge to bridge RFID channel 2021-11-15 17:53:59 +01:00
489cc492d5
Deploy garage v0.4.0 2021-11-10 14:19:23 +01:00
90 changed files with 1264 additions and 985 deletions

3
.gitmodules vendored
View file

@ -1,6 +1,3 @@
[submodule "docker/static/goStatic"]
path = app/build/static/goStatic
url = https://github.com/PierreZ/goStatic
[submodule "docker/blog/quentin.dufour.io"]
path = docker/blog-quentin/quentin.dufour.io
url = git@gitlab.com:superboum/quentin.dufour.io.git

View file

@ -1,31 +1,8 @@
deuxfleurs.fr
=============
*Many things are still missing here, including a proper documentation. Please stay nice, it is a volunter project. Feel free to open pull/merge requests to improve it. Thanks.*
## Our abstraction stack
We try to build a generic abstraction stack between our different resources (CPU, RAM, disk, etc.) and our services (Chat, Storage, etc.), we develop our own tools when needed:
* **[garage](https://git.deuxfleurs.fr/Deuxfleurs/garage/):** S3-compatible lightweight object store for self-hosted geo-distributed deployments (we also have a legacy glusterfs cluster)
* **[diplonat](https://git.deuxfleurs.fr/Deuxfleurs/diplonat):** network automation (firewalling, upnp igd)
* **[bottin](https://git.deuxfleurs.fr/Deuxfleurs/bottin):** authentication and authorization (LDAP protocol, consul backend)
* **[guichet](https://git.deuxfleurs.fr/Deuxfleurs/guichet):** a dashboard for our users and administrators
* **ansible:** physical node configuration
* **nomad:** schedule containers and handle their lifecycle
* **consul:** distributed key value store + lock + service discovery
* **stolon + postgresql:** distributed relational database
* **docker:** package, distribute and isolate applications
Some services we provide:
* **Websites:** garage (static) + fediverse blog (plume)
* **Chat:** Synapse + Element Web (Matrix protocol)
* **Email:** Postfix SMTP + Dovecot IMAP + opendkim DKIM + Sogo webmail (legacy) | Alps webmail (experimental)
* **Storage:** Seafile (legacy) | Nextcloud (experimental)
* **Visio:** Jitsi
As a generic abstraction is provided, deploying new services should be easy.
**OBSOLETION NOTICE:** We are progressively migrating our stack to NixOS, to replace Ansible. Most of the files present in this repository are outdated or obsolete,
the current code for our infrastructure is at: <https://git.deuxfleurs.fr/Deuxfleurs/nixcfg>.
## I am lost, how this repo works?
@ -42,73 +19,3 @@ To ease the development, we make the choice of a fully integrated environment
3. `op_guide`: Guides to explain you operations you can do cluster wide (like configuring postgres)
## Start hacking
### Deploying/Updating new services is done from your machine
*The following instructions are provided for ops that already have access to the servers (meaning: their SSH public key is known by the cluster).*
Deploy Nomad on your machine:
```bash
export NOMAD_VER=1.0.1
wget https://releases.hashicorp.com/nomad/${NOMAD_VER}/nomad_${NOMAD_VER}_linux_amd64.zip
unzip nomad_${NOMAD_VER}_linux_amd64.zip
sudo mv nomad /usr/local/bin
rm nomad_${NOMAD_VER}_linux_amd64.zip
```
Deploy Consul on your machine:
```bash
export CONSUL_VER=1.9.0
wget https://releases.hashicorp.com/consul/${CONSUL_VER}/consul_${CONSUL_VER}_linux_amd64.zip
unzip consul_${CONSUL_VER}_linux_amd64.zip
sudo mv consul /usr/local/bin
rm consul_${CONSUL_VER}_linux_amd64.zip
```
Create an alias (and put it in your `.bashrc`) to bind APIs on your machine:
```
alias bind_df="ssh \
-p110 \
-N \
-L 1389:bottin2.service.2.cluster.deuxfleurs.fr:389 \
-L 4646:127.0.0.1:4646 \
-L 5432:psql-proxy.service.2.cluster.deuxfleurs.fr:5432 \
-L 8082:traefik-admin.service.2.cluster.deuxfleurs.fr:8082 \
-L 8500:127.0.0.1:8500 \
<a server from the cluster>"
```
and run:
bind_df
Adrien uses `.ssh/config` configuration instead. I works basically the same. Here it goes:
```
# in ~/.ssh/config
Host deuxfleurs
User adrien
Hostname deuxfleurs.fr
# If you don't use the default ~/.ssh/id_rsa to connect to Deuxfleurs
IdentityFile <some_key_path>
PubKeyAuthentication yes
ForwardAgent No
LocalForward 1389 bottin2.service.2.cluster.deuxfleurs.fr:389
LocalForward 4646 127.0.0.1:4646
LocalForward 5432 psql-proxy.service.2.cluster.deuxfleurs.fr:5432
LocalForward 8082 traefik-admin.service.2.cluster.deuxfleurs.fr:8082
LocalForward 8500 127.0.0.1:8500
```
Now, to connect, do the following:
ssh deuxfleurs -N
## Test cluster
Configured machines available for testing are listed in the **[`test_cluster` Ansible inventory](./os/config/test_cluster.inventory.yml)**.

View file

@ -1,22 +0,0 @@
FROM golang:buster as builder
WORKDIR /root
RUN git clone https://filippo.io/age && cd age/cmd/age && go build -o age .
FROM amd64/debian:buster
COPY --from=builder /root/age/cmd/age/age /usr/local/bin/age
RUN apt-get update && \
apt-get -qq -y full-upgrade && \
apt-get install -y rsync wget openssh-client postgresql-client && \
apt-get clean && \
rm -f /var/lib/apt/lists/*_*
RUN mkdir -p /root/.ssh
WORKDIR /root
COPY do_backup.sh /root/do_backup.sh
CMD "/root/do_backup.sh"

View file

@ -1,40 +0,0 @@
#!/bin/sh
set -x -e
cd /root
chmod 0600 .ssh/id_ed25519
cat > .ssh/config <<EOF
Host backuphost
HostName $TARGET_SSH_HOST
Port $TARGET_SSH_PORT
User $TARGET_SSH_USER
EOF
echo "export sql"
export PGPASSWORD=$REPL_PSQL_PWD
pg_basebackup \
--pgdata=- \
--format=tar \
--max-rate=1M \
--no-slot \
--wal-method=none \
--gzip \
--compress=8 \
--checkpoint=spread \
--progress \
--verbose \
--status-interval=10 \
--username=$REPL_PSQL_USER \
--port=5432 \
--host=psql-proxy.service.2.cluster.deuxfleurs.fr | \
age -r "$(cat /root/.ssh/id_ed25519.pub)" | \
ssh backuphost "cat > $TARGET_SSH_DIR/matrix/db-$(date --iso-8601=minute).gz.age"
MATRIX_MEDIA="/mnt/glusterfs/chat/matrix/synapse/media"
echo "export local_content"
tar -vzcf - ${MATRIX_MEDIA} | \
age -r "$(cat /root/.ssh/id_ed25519.pub)" | \
ssh backuphost "cat > $TARGET_SSH_DIR/matrix/media-$(date --iso-8601=minute).gz.age"

View file

@ -0,0 +1 @@
result

View file

@ -0,0 +1,8 @@
## Build
```bash
docker load < $(nix-build docker.nix)
docker push superboum/backup-psql:???
```

View file

@ -0,0 +1,106 @@
#!/usr/bin/env python3
import shutil,sys,os,datetime,minio,subprocess
working_directory = "."
if 'CACHE_DIR' in os.environ: working_directory = os.environ['CACHE_DIR']
required_space_in_bytes = 20 * 1024 * 1024 * 1024
bucket = os.environ['AWS_BUCKET']
key = os.environ['AWS_ACCESS_KEY_ID']
secret = os.environ['AWS_SECRET_ACCESS_KEY']
endpoint = os.environ['AWS_ENDPOINT']
pubkey = os.environ['CRYPT_PUBLIC_KEY']
psql_host = os.environ['PSQL_HOST']
psql_user = os.environ['PSQL_USER']
s3_prefix = str(datetime.datetime.now())
files = [ "backup_manifest", "base.tar.gz", "pg_wal.tar.gz" ]
clear_paths = [ os.path.join(working_directory, f) for f in files ]
crypt_paths = [ os.path.join(working_directory, f) + ".age" for f in files ]
s3_keys = [ s3_prefix + "/" + f for f in files ]
def abort(msg):
for p in clear_paths + crypt_paths:
if os.path.exists(p):
print(f"Remove {p}")
os.remove(p)
if msg: sys.exit(msg)
else: print("success")
# Check we have enough space on disk
if shutil.disk_usage(working_directory).free < required_space_in_bytes:
abort(f"Not enough space on disk at path {working_directory} to perform a backup, aborting")
# Check postgres password is set
if 'PGPASSWORD' not in os.environ:
abort(f"You must pass postgres' password through the environment variable PGPASSWORD")
# Check our working directory is empty
if len(os.listdir(working_directory)) != 0:
abort(f"Working directory {working_directory} is not empty, aborting")
# Check Minio
client = minio.Minio(endpoint, key, secret)
if not client.bucket_exists(bucket):
abort(f"Bucket {bucket} does not exist or its access is forbidden, aborting")
# Perform the backup locally
try:
ret = subprocess.run(["pg_basebackup",
f"--host={psql_host}",
f"--username={psql_user}",
f"--pgdata={working_directory}",
f"--format=tar",
"--wal-method=stream",
"--gzip",
"--compress=6",
"--progress",
"--max-rate=5M",
])
if ret.returncode != 0:
abort(f"pg_basebackup exited, expected return code 0, got {ret.returncode}. aborting")
except Exception as e:
abort(f"pg_basebackup raised exception {e}. aborting")
# Check that the expected files are here
for p in clear_paths:
print(f"Checking that {p} exists locally")
if not os.path.exists(p):
abort(f"File {p} expected but not found, aborting")
# Cipher them
for c, e in zip(clear_paths, crypt_paths):
print(f"Ciphering {c} to {e}")
try:
ret = subprocess.run(["age", "-r", pubkey, "-o", e, c])
if ret.returncode != 0:
abort(f"age exit code is {ret}, 0 expected. aborting")
except Exception as e:
abort(f"aged raised an exception. {e}. aborting")
# Upload the backup to S3
for p, k in zip(crypt_paths, s3_keys):
try:
print(f"Uploading {p} to {k}")
result = client.fput_object(bucket, k, p)
print(
"created {0} object; etag: {1}, version-id: {2}".format(
result.object_name, result.etag, result.version_id,
),
)
except Exception as e:
abort(f"Exception {e} occured while upload {p}. aborting")
# Check that the files have been uploaded
for k in s3_keys:
try:
print(f"Checking that {k} exists remotely")
result = client.stat_object(bucket, k)
print(
"last-modified: {0}, size: {1}".format(
result.last_modified, result.size,
),
)
except Exception as e:
abort(f"{k} not found on S3. {e}. aborting")
abort(None)

View file

@ -0,0 +1,8 @@
{
pkgsSrc = fetchTarball {
# Latest commit on https://github.com/NixOS/nixpkgs/tree/nixos-21.11
# As of 2022-04-15
url ="https://github.com/NixOS/nixpkgs/archive/2f06b87f64bc06229e05045853e0876666e1b023.tar.gz";
sha256 = "sha256:1d7zg96xw4qsqh7c89pgha9wkq3rbi9as3k3d88jlxy2z0ns0cy2";
};
}

View file

@ -0,0 +1,37 @@
let
common = import ./common.nix;
pkgs = import common.pkgsSrc {};
python-with-my-packages = pkgs.python3.withPackages (p: with p; [
minio
]);
in
pkgs.stdenv.mkDerivation {
name = "backup-psql";
src = pkgs.lib.sourceFilesBySuffices ./. [ ".py" ];
buildInputs = [
python-with-my-packages
pkgs.age
pkgs.postgresql_14
];
buildPhase = ''
cat > backup-psql <<EOF
#!${pkgs.bash}/bin/bash
export PYTHONPATH=${python-with-my-packages}/${python-with-my-packages.sitePackages}
export PATH=${python-with-my-packages}/bin:${pkgs.age}/bin:${pkgs.postgresql_14}/bin
${python-with-my-packages}/bin/python3 $out/lib/backup-psql.py
EOF
chmod +x backup-psql
'';
installPhase = ''
mkdir -p $out/{bin,lib}
cp *.py $out/lib/backup-psql.py
cp backup-psql $out/bin/backup-psql
'';
}

View file

@ -0,0 +1,11 @@
let
common = import ./common.nix;
app = import ./default.nix;
pkgs = import common.pkgsSrc {};
in
pkgs.dockerTools.buildImage {
name = "superboum/backup-psql-docker";
config = {
Cmd = [ "${app}/bin/backup-psql" ];
};
}

View file

@ -0,0 +1,171 @@
job "backup_daily" {
datacenters = ["dc1"]
type = "batch"
priority = "60"
periodic {
cron = "@daily"
// Do not allow overlapping runs.
prohibit_overlap = true
}
group "backup-dovecot" {
constraint {
attribute = "${attr.unique.hostname}"
operator = "="
value = "digitale"
}
task "main" {
driver = "docker"
config {
image = "restic/restic:0.12.1"
entrypoint = [ "/bin/sh", "-c" ]
args = [ "restic backup /mail && restic forget --keep-within 1m1d --keep-within-weekly 3m --keep-within-monthly 1y && restic prune --max-unused 50% --max-repack-size 2G && restic check" ]
volumes = [
"/mnt/ssd/mail:/mail"
]
}
template {
data = <<EOH
AWS_ACCESS_KEY_ID={{ key "secrets/email/dovecot/backup_aws_access_key_id" }}
AWS_SECRET_ACCESS_KEY={{ key "secrets/email/dovecot/backup_aws_secret_access_key" }}
RESTIC_REPOSITORY={{ key "secrets/email/dovecot/backup_restic_repository" }}
RESTIC_PASSWORD={{ key "secrets/email/dovecot/backup_restic_password" }}
EOH
destination = "secrets/env_vars"
env = true
}
resources {
cpu = 500
memory = 200
}
restart {
attempts = 2
interval = "30m"
delay = "15s"
mode = "fail"
}
}
}
group "backup-plume" {
constraint {
attribute = "${attr.unique.hostname}"
operator = "="
value = "digitale"
}
task "main" {
driver = "docker"
config {
image = "restic/restic:0.12.1"
entrypoint = [ "/bin/sh", "-c" ]
args = [ "restic backup /plume && restic forget --keep-within 1m1d --keep-within-weekly 3m --keep-within-monthly 1y && restic prune --max-unused 50% --max-repack-size 2G && restic check" ]
volumes = [
"/mnt/ssd/plume/media:/plume"
]
}
template {
data = <<EOH
AWS_ACCESS_KEY_ID={{ key "secrets/plume/backup_aws_access_key_id" }}
AWS_SECRET_ACCESS_KEY={{ key "secrets/plume/backup_aws_secret_access_key" }}
RESTIC_REPOSITORY={{ key "secrets/plume/backup_restic_repository" }}
RESTIC_PASSWORD={{ key "secrets/plume/backup_restic_password" }}
EOH
destination = "secrets/env_vars"
env = true
}
resources {
cpu = 500
memory = 200
}
restart {
attempts = 2
interval = "30m"
delay = "15s"
mode = "fail"
}
}
}
group "backup-consul" {
task "consul-kv-export" {
driver = "docker"
lifecycle {
hook = "prestart"
sidecar = false
}
config {
image = "consul:1.11.2"
network_mode = "host"
entrypoint = [ "/bin/sh", "-c" ]
args = [ "/bin/consul kv export > $NOMAD_ALLOC_DIR/consul.json" ]
}
env {
CONSUL_HTTP_ADDR = "http://consul.service.2.cluster.deuxfleurs.fr:8500"
}
resources {
cpu = 200
memory = 200
}
restart {
attempts = 2
interval = "30m"
delay = "15s"
mode = "fail"
}
}
task "restic-backup" {
driver = "docker"
config {
image = "restic/restic:0.12.1"
entrypoint = [ "/bin/sh", "-c" ]
args = [ "restic backup $NOMAD_ALLOC_DIR/consul.json && restic forget --keep-within 1m1d --keep-within-weekly 3m --keep-within-monthly 1y && restic prune --max-unused 50% --max-repack-size 2G && restic check" ]
}
template {
data = <<EOH
AWS_ACCESS_KEY_ID={{ key "secrets/backup/consul/backup_aws_access_key_id" }}
AWS_SECRET_ACCESS_KEY={{ key "secrets/backup/consul/backup_aws_secret_access_key" }}
RESTIC_REPOSITORY={{ key "secrets/backup/consul/backup_restic_repository" }}
RESTIC_PASSWORD={{ key "secrets/backup/consul/backup_restic_password" }}
EOH
destination = "secrets/env_vars"
env = true
}
resources {
cpu = 200
memory = 200
}
restart {
attempts = 2
interval = "30m"
delay = "15s"
mode = "fail"
}
}
}
}

View file

@ -1,62 +0,0 @@
job "backup_manual_matrix" {
datacenters = ["dc1"]
type = "batch"
task "backup-matrix" {
driver = "docker"
config {
image = "superboum/backup_matrix:4"
volumes = [
"secrets/id_ed25519:/root/.ssh/id_ed25519",
"secrets/id_ed25519.pub:/root/.ssh/id_ed25519.pub",
"secrets/known_hosts:/root/.ssh/known_hosts",
"/mnt/glusterfs/chat/matrix/synapse/media:/mnt/glusterfs/chat/matrix/synapse/media"
]
network_mode = "host"
}
env {
CONSUL_HTTP_ADDR = "http://consul.service.2.cluster.deuxfleurs.fr:8500"
}
template {
data = <<EOH
TARGET_SSH_USER={{ key "secrets/backup/target_ssh_user" }}
TARGET_SSH_PORT={{ key "secrets/backup/target_ssh_port" }}
TARGET_SSH_HOST={{ key "secrets/backup/target_ssh_host" }}
TARGET_SSH_DIR={{ key "secrets/backup/target_ssh_dir" }}
REPL_PSQL_USER={{ key "secrets/postgres/keeper/pg_repl_username" }}
REPL_PSQL_PWD={{ key "secrets/postgres/keeper/pg_repl_pwd" }}
EOH
destination = "secrets/env_vars"
env = true
}
template {
data = "{{ key \"secrets/backup/id_ed25519\" }}"
destination = "secrets/id_ed25519"
}
template {
data = "{{ key \"secrets/backup/id_ed25519.pub\" }}"
destination = "secrets/id_ed25519.pub"
}
template {
data = "{{ key \"secrets/backup/target_ssh_fingerprint\" }}"
destination = "secrets/known_hosts"
}
resources {
memory = 200
}
restart {
attempts = 2
interval = "30m"
delay = "15s"
mode = "fail"
}
}
}

View file

@ -0,0 +1,55 @@
job "backup_weekly" {
datacenters = ["dc1"]
type = "batch"
priority = "60"
periodic {
cron = "@weekly"
// Do not allow overlapping runs.
prohibit_overlap = true
}
group "backup-psql" {
task "main" {
driver = "docker"
config {
image = "superboum/backup-psql-docker:gyr3aqgmhs0hxj0j9hkrdmm1m07i8za2"
volumes = [
// Mount a cache on the hard disk to avoid filling the SSD
"/mnt/storage/tmp_bckp_psql:/mnt/cache"
]
}
template {
data = <<EOH
CACHE_DIR=/mnt/cache
AWS_BUCKET=backups-pgbasebackup
AWS_ENDPOINT=s3.deuxfleurs.shirokumo.net
AWS_ACCESS_KEY_ID={{ key "secrets/backup/psql/aws_access_key_id" }}
AWS_SECRET_ACCESS_KEY={{ key "secrets/backup/psql/aws_secret_access_key" }}
CRYPT_PUBLIC_KEY={{ key "secrets/backup/psql/crypt_public_key" }}
PSQL_HOST=psql-proxy.service.2.cluster.deuxfleurs.fr
PSQL_USER={{ key "secrets/postgres/keeper/pg_repl_username" }}
PGPASSWORD={{ key "secrets/postgres/keeper/pg_repl_pwd" }}
EOH
destination = "secrets/env_vars"
env = true
}
resources {
cpu = 200
memory = 200
}
restart {
attempts = 2
interval = "30m"
delay = "15s"
mode = "fail"
}
}
}
}

View file

@ -0,0 +1 @@
USER Backup AWS access key ID

View file

@ -0,0 +1 @@
USER Backup AWS secret access key

View file

@ -0,0 +1 @@
USER Restic password to encrypt backups

View file

@ -0,0 +1 @@
USER Restic repository, eg. s3:https://s3.garage.tld

View file

@ -0,0 +1 @@
USER Minio access key

View file

@ -0,0 +1 @@
USER Minio secret key

View file

@ -0,0 +1 @@
USER a private key to decript backups from age

View file

@ -0,0 +1 @@
USER A public key to encypt backups with age

View file

@ -13,14 +13,21 @@ job "bagage" {
network {
port "web_port" { to = 8080 }
port "ssh_port" {
static = 2222
to = 2222
}
}
task "server" {
driver = "docker"
config {
image = "superboum/amd64_bagage:v8"
readonly_rootfs = true
ports = [ "web_port" ]
image = "superboum/amd64_bagage:v11"
readonly_rootfs = false
volumes = [
"secrets/id_rsa:/id_rsa"
]
ports = [ "web_port", "ssh_port" ]
}
env {
@ -31,19 +38,36 @@ job "bagage" {
memory = 500
}
template {
data = "{{ key \"secrets/bagage/id_rsa\" }}"
destination = "secrets/id_rsa"
}
service {
name = "bagage"
name = "bagage-ssh"
port = "ssh_port"
address_mode = "host"
tags = [
"bagage",
"(diplonat (tcp_port 2222))"
]
}
service {
name = "bagage-webdav"
tags = [
"bagage",
"traefik.enable=true",
"traefik.frontend.entryPoints=https,http",
"traefik.frontend.rule=Host:bagage.deuxfleurs.fr",
"tricot bagage.deuxfleurs.fr",
]
port = "web_port"
address_mode = "host"
check {
type = "tcp"
port = "web_port"
address_mode = "host"
interval = "60s"
timeout = "5s"
check_restart {

View file

@ -0,0 +1 @@
CMD ssh-keygen -q -f >(cat) -N "" <<< y 2>/dev/null 1>&2 ; true

View file

@ -1,5 +1,5 @@
job "core" {
datacenters = ["dc1"]
datacenters = ["dc1", "neptune"]
type = "system"
priority = 90
@ -18,7 +18,7 @@ job "core" {
driver = "docker"
config {
image = "darkgallium/amd64_diplonat:v3"
image = "lxpz/amd64_diplonat:3"
network_mode = "host"
readonly_rootfs = true
privileged = true
@ -33,7 +33,6 @@ job "core" {
template {
data = <<EOH
DIPLONAT_PRIVATE_IP={{ env "attr.unique.network.ip-address" }}
DIPLONAT_REFRESH_TIME=60
DIPLONAT_EXPIRATION_TIME=300
DIPLONAT_CONSUL_NODE_NAME={{ env "attr.unique.hostname" }}

View file

@ -0,0 +1,2 @@
docker load < $(nix-build docker.nix)
docker push superboum/cryptpad:???

View file

@ -0,0 +1,8 @@
{
pkgsSrc = fetchTarball {
# Latest commit on https://github.com/NixOS/nixpkgs/tree/nixos-21.11
# As of 2022-04-15
url ="https://github.com/NixOS/nixpkgs/archive/2f06b87f64bc06229e05045853e0876666e1b023.tar.gz";
sha256 = "sha256:1d7zg96xw4qsqh7c89pgha9wkq3rbi9as3k3d88jlxy2z0ns0cy2";
};
}

View file

@ -0,0 +1,10 @@
let
common = import ./common.nix;
pkgs = import common.pkgsSrc {};
in
pkgs.dockerTools.buildImage {
name = "superboum/cryptpad";
config = {
Cmd = [ "${pkgs.cryptpad}/bin/cryptpad" ];
};
}

View file

@ -0,0 +1,283 @@
/* globals module */
/* DISCLAIMER:
There are two recommended methods of running a CryptPad instance:
1. Using a standalone nodejs server without HTTPS (suitable for local development)
2. Using NGINX to serve static assets and to handle HTTPS for API server's websocket traffic
We do not officially recommend or support Apache, Docker, Kubernetes, Traefik, or any other configuration.
Support requests for such setups should be directed to their authors.
If you're having difficulty difficulty configuring your instance
we suggest that you join the project's IRC/Matrix channel.
If you don't have any difficulty configuring your instance and you'd like to
support us for the work that went into making it pain-free we are quite happy
to accept donations via our opencollective page: https://opencollective.com/cryptpad
*/
module.exports = {
/* CryptPad is designed to serve its content over two domains.
* Account passwords and cryptographic content is handled on the 'main' domain,
* while the user interface is loaded on a 'sandbox' domain
* which can only access information which the main domain willingly shares.
*
* In the event of an XSS vulnerability in the UI (that's bad)
* this system prevents attackers from gaining access to your account (that's good).
*
* Most problems with new instances are related to this system blocking access
* because of incorrectly configured sandboxes. If you only see a white screen
* when you try to load CryptPad, this is probably the cause.
*
* PLEASE READ THE FOLLOWING COMMENTS CAREFULLY.
*
*/
/* httpUnsafeOrigin is the URL that clients will enter to load your instance.
* Any other URL that somehow points to your instance is supposed to be blocked.
* The default provided below assumes you are loading CryptPad from a server
* which is running on the same machine, using port 3000.
*
* In a production instance this should be available ONLY over HTTPS
* using the default port for HTTPS (443) ie. https://cryptpad.fr
* In such a case this should be also handled by NGINX, as documented in
* cryptpad/docs/example.nginx.conf (see the $main_domain variable)
*
*/
httpUnsafeOrigin: 'http://localhost:3000',
/* httpSafeOrigin is the URL that is used for the 'sandbox' described above.
* If you're testing or developing with CryptPad on your local machine then
* it is appropriate to leave this blank. The default behaviour is to serve
* the main domain over port 3000 and to serve the sandbox content over port 3001.
*
* This is not appropriate in a production environment where invasive networks
* may filter traffic going over abnormal ports.
* To correctly configure your production instance you must provide a URL
* with a different domain (a subdomain is sufficient).
* It will be used to load the UI in our 'sandbox' system.
*
* This value corresponds to the $sandbox_domain variable
* in the example nginx file.
*
* Note that in order for the sandboxing system to be effective
* httpSafeOrigin must be different from httpUnsafeOrigin.
*
* CUSTOMIZE AND UNCOMMENT THIS FOR PRODUCTION INSTALLATIONS.
*/
// httpSafeOrigin: "https://some-other-domain.xyz",
/* httpAddress specifies the address on which the nodejs server
* should be accessible. By default it will listen on 127.0.0.1
* (IPv4 localhost on most systems). If you want it to listen on
* all addresses, including IPv6, set this to '::'.
*
*/
httpAddress: '::',
/* httpPort specifies on which port the nodejs server should listen.
* By default it will serve content over port 3000, which is suitable
* for both local development and for use with the provided nginx example,
* which will proxy websocket traffic to your node server.
*
*/
//httpPort: 3000,
/* httpSafePort allows you to specify an alternative port from which
* the node process should serve sandboxed assets. The default value is
* that of your httpPort + 1. You probably don't need to change this.
*
*/
//httpSafePort: 3001,
/* CryptPad will launch a child process for every core available
* in order to perform CPU-intensive tasks in parallel.
* Some host environments may have a very large number of cores available
* or you may want to limit how much computing power CryptPad can take.
* If so, set 'maxWorkers' to a positive integer.
*/
// maxWorkers: 4,
/* =====================
* Admin
* ===================== */
/*
* CryptPad contains an administration panel. Its access is restricted to specific
* users using the following list.
* To give access to the admin panel to a user account, just add their public signing
* key, which can be found on the settings page for registered users.
* Entries should be strings separated by a comma.
*/
/*
adminKeys: [
//"[cryptpad-user1@my.awesome.website/YZgXQxKR0Rcb6r6CmxHPdAGLVludrAF2lEnkbx1vVOo=]",
],
*/
/* =====================
* STORAGE
* ===================== */
/* Pads that are not 'pinned' by any registered user can be set to expire
* after a configurable number of days of inactivity (default 90 days).
* The value can be changed or set to false to remove expiration.
* Expired pads can then be removed using a cron job calling the
* `evict-inactive.js` script with node
*
* defaults to 90 days if nothing is provided
*/
//inactiveTime: 90, // days
/* CryptPad archives some data instead of deleting it outright.
* This archived data still takes up space and so you'll probably still want to
* remove these files after a brief period.
*
* cryptpad/scripts/evict-inactive.js is intended to be run daily
* from a crontab or similar scheduling service.
*
* The intent with this feature is to provide a safety net in case of accidental
* deletion. Set this value to the number of days you'd like to retain
* archived data before it's removed permanently.
*
* defaults to 15 days if nothing is provided
*/
//archiveRetentionTime: 15,
/* It's possible to configure your instance to remove data
* stored on behalf of inactive accounts. Set 'accountRetentionTime'
* to the number of days an account can remain idle before its
* documents and other account data is removed.
*
* Leave this value commented out to preserve all data stored
* by user accounts regardless of inactivity.
*/
//accountRetentionTime: 365,
/* Starting with CryptPad 3.23.0, the server automatically runs
* the script responsible for removing inactive data according to
* your configured definition of inactivity. Set this value to `true`
* if you prefer not to remove inactive data, or if you prefer to
* do so manually using `scripts/evict-inactive.js`.
*/
//disableIntegratedEviction: true,
/* Max Upload Size (bytes)
* this sets the maximum size of any one file uploaded to the server.
* anything larger than this size will be rejected
* defaults to 20MB if no value is provided
*/
//maxUploadSize: 20 * 1024 * 1024,
/* Users with premium accounts (those with a plan included in their customLimit)
* can benefit from an increased upload size limit. By default they are restricted to the same
* upload size as any other registered user.
*
*/
//premiumUploadSize: 100 * 1024 * 1024,
/* =====================
* DATABASE VOLUMES
* ===================== */
/*
* CryptPad stores each document in an individual file on your hard drive.
* Specify a directory where files should be stored.
* It will be created automatically if it does not already exist.
*/
filePath: './root/tmp/mut/datastore/',
/* CryptPad offers the ability to archive data for a configurable period
* before deleting it, allowing a means of recovering data in the event
* that it was deleted accidentally.
*
* To set the location of this archive directory to a custom value, change
* the path below:
*/
archivePath: './root/tmp/mut/data/archive',
/* CryptPad allows logged in users to request that particular documents be
* stored by the server indefinitely. This is called 'pinning'.
* Pin requests are stored in a pin-store. The location of this store is
* defined here.
*/
pinPath: './root/tmp/mut/data/pins',
/* if you would like the list of scheduled tasks to be stored in
a custom location, change the path below:
*/
taskPath: './root/tmp/mut/data/tasks',
/* if you would like users' authenticated blocks to be stored in
a custom location, change the path below:
*/
blockPath: './root/tmp/mut/block',
/* CryptPad allows logged in users to upload encrypted files. Files/blobs
* are stored in a 'blob-store'. Set its location here.
*/
blobPath: './root/tmp/mut/blob',
/* CryptPad stores incomplete blobs in a 'staging' area until they are
* fully uploaded. Set its location here.
*/
blobStagingPath: './root/tmp/mut/data/blobstage',
decreePath: './root/tmp/mut/data/decrees',
/* CryptPad supports logging events directly to the disk in a 'logs' directory
* Set its location here, or set it to false (or nothing) if you'd rather not log
*/
logPath: './root/tmp/mut/data/logs',
/* =====================
* Debugging
* ===================== */
/* CryptPad can log activity to stdout
* This may be useful for debugging
*/
logToStdout: true,
/* CryptPad can be configured to log more or less
* the various settings are listed below by order of importance
*
* silly, verbose, debug, feedback, info, warn, error
*
* Choose the least important level of logging you wish to see.
* For example, a 'silly' logLevel will display everything,
* while 'info' will display 'info', 'warn', and 'error' logs
*
* This will affect both logging to the console and the disk.
*/
logLevel: 'debug',
/* clients can use the /settings/ app to opt out of usage feedback
* which informs the server of things like how much each app is being
* used, and whether certain clientside features are supported by
* the client's browser. The intent is to provide feedback to the admin
* such that the service can be improved. Enable this with `true`
* and ignore feedback with `false` or by commenting the attribute
*
* You will need to set your logLevel to include 'feedback'. Set this
* to false if you'd like to exclude feedback from your logs.
*/
logFeedback: false,
/* CryptPad supports verbose logging
* (false by default)
*/
verbose: true,
/* Surplus information:
*
* 'installMethod' is included in server telemetry to voluntarily
* indicate how many instances are using unofficial installation methods
* such as Docker.
*
*/
installMethod: 'unspecified',
};

View file

@ -4,7 +4,7 @@
"consul_host": "http://consul.service.2.cluster.deuxfleurs.fr:8500",
"log_level": "debug",
"acl": [
"*,dc=deuxfleurs,dc=fr::read:*:* !userpassword",
"*,dc=deuxfleurs,dc=fr::read:*:* !userpassword !user_secret !alternate_user_secrets !garage_s3_secret_key",
"*::read modify:SELF:*",
"ANONYMOUS::bind:*,ou=users,dc=deuxfleurs,dc=fr:",
"ANONYMOUS::bind:cn=admin,dc=deuxfleurs,dc=fr:",
@ -20,10 +20,6 @@
"*:cn=asso_deuxfleurs,ou=groups,dc=deuxfleurs,dc=fr:modifyAdd:cn=email,ou=groups,dc=deuxfleurs,dc=fr:*",
"*,ou=invitations,dc=deuxfleurs,dc=fr::modifyAdd:cn=email,ou=groups,dc=deuxfleurs,dc=fr:*",
"*:cn=asso_deuxfleurs,ou=groups,dc=deuxfleurs,dc=fr:modifyAdd:cn=seafile,ou=groups,dc=deuxfleurs,dc=fr:*",
"*,ou=invitations,dc=deuxfleurs,dc=fr::modifyAdd:cn=seafile,ou=groups,dc=deuxfleurs,dc=fr:*",
"*:cn=asso_deuxfleurs,ou=groups,dc=deuxfleurs,dc=fr:modifyAdd:cn=nextcloud,ou=groups,dc=deuxfleurs,dc=fr:*",
"*,ou=invitations,dc=deuxfleurs,dc=fr::modifyAdd:cn=seafile,ou=nextcloud,dc=deuxfleurs,dc=fr:*",
"cn=admin,dc=deuxfleurs,dc=fr::read add modify delete:*:*",
"*:cn=admin,ou=groups,dc=deuxfleurs,dc=fr:read add modify delete:*:*"

View file

@ -59,6 +59,7 @@ job "directory" {
}
}
/*
group "guichet" {
count = 1
@ -69,7 +70,7 @@ job "directory" {
task "guichet" {
driver = "docker"
config {
image = "superboum/guichet_amd64:15"
image = "dxflrs/guichet:6y7pv4kgfsn02iijj55kf5af0rbksgrn"
readonly_rootfs = true
ports = [ "web_port" ]
volumes = [
@ -93,6 +94,7 @@ job "directory" {
"traefik.enable=true",
"traefik.frontend.entryPoints=https,http",
"traefik.frontend.rule=Host:guichet.deuxfleurs.fr",
"tricot guichet.deuxfleurs.fr",
]
port = "web_port"
address_mode = "host"
@ -110,5 +112,6 @@ job "directory" {
}
}
}
*/
}

View file

@ -7,18 +7,21 @@ services:
context: ./im/build/riotweb
args:
# https://github.com/vector-im/riot-web/releases
VERSION: 1.9.0
image: superboum/amd64_riotweb:v25
VERSION: 1.10.15
image: superboum/amd64_riotweb:v30
synapse:
build:
context: ./im/build/matrix-synapse
args:
# https://github.com/matrix-org/synapse/releases
VERSION: 1.42.0
VERSION: 1.61.1
# https://github.com/matrix-org/synapse-s3-storage-provider/commits/main
S3_VERSION: 3c3fafd6a2624f05fd396d9e003501bf8bef7b2e
image: superboum/amd64_synapse:v47
# Update with the latest commit on main each time you update the synapse version
# otherwise synapse may fail to launch due to incompatibility issues
# see this issue for an example: https://github.com/matrix-org/synapse-s3-storage-provider/issues/64
S3_VERSION: ffd3fa477321608e57d27644197e721965e0e858
image: superboum/amd64_synapse:v53
# Email
sogo:
@ -47,16 +50,16 @@ services:
context: ./jitsi/build/jitsi-meet
args:
# https://github.com/jitsi/jitsi-meet
MEET_TAG: jitsi-meet_5463
image: superboum/amd64_jitsi_meet:v4
MEET_TAG: stable/jitsi-meet_6826
image: superboum/amd64_jitsi_meet:v5
jitsi-conference-focus:
build:
context: ./jitsi/build/jitsi-conference-focus
args:
# https://github.com/jitsi/jicofo
JICOFO_TAG: jitsi-meet_5463
image: superboum/amd64_jitsi_conference_focus:v7
JICOFO_TAG: stable/jitsi-meet_6826
image: superboum/amd64_jitsi_conference_focus:v9
jitsi-videobridge:
build:
@ -64,23 +67,23 @@ services:
args:
# https://github.com/jitsi/jitsi-videobridge
# note: JVB is not tagged with non-stable tags
JVB_TAG: stable/jitsi-meet_5390
image: superboum/amd64_jitsi_videobridge:v17
JVB_TAG: stable/jitsi-meet_6826
image: superboum/amd64_jitsi_videobridge:v20
jitsi-xmpp:
build:
context: ./jitsi/build/jitsi-xmpp
args:
MEET_TAG: jitsi-meet_5463
PROSODY_VERSION: 0.11.7-1~buster4
image: superboum/amd64_jitsi_xmpp:v9
MEET_TAG: stable/jitsi-meet_6826
PROSODY_VERSION: 0.11.12-1
image: superboum/amd64_jitsi_xmpp:v10
plume:
build:
context: ./plume/build/plume
args:
VERSION: 8c372aa6fcd05083601903d83b0fcb4915585a95
image: superboum/plume:v4
VERSION: 8709f6cf9f8ff7e3c5ee7ea699ee7c778e92fefc
image: superboum/plume:v8
postfix:
build:
@ -94,16 +97,12 @@ services:
build:
args:
# https://github.com/sorintlab/stolon/releases
STOLON_VERSION: 057389f7e484ee1d5c1e1a7020256020e7413c87
STOLON_VERSION: 3bb7499f815f77140551eb762b200cf4557f57d3
context: ./postgres/build/postgres
image: superboum/amd64_postgres:v9
image: superboum/amd64_postgres:v11
backup-consul:
build:
context: ./backup/build/backup-consul
image: lxpz/backup_consul:12
backup-matrix:
build:
context: ./backup/build/backup-matrix
image: superboum/backup_matrix:4

View file

@ -14,7 +14,7 @@ job "drone-ci" {
task "drone_server" {
driver = "docker"
config {
image = "drone/drone:2.4.0"
image = "drone/drone:2.12.0"
ports = [ "web_port" ]
}
@ -38,6 +38,7 @@ DRONE_S3_PATH_STYLE=true
DRONE_DATABASE_DRIVER=postgres
DRONE_DATABASE_DATASOURCE=postgres://{{ key "secrets/drone-ci/db_user" }}:{{ key "secrets/drone-ci/db_pass" }}@psql-proxy.service.2.cluster.deuxfleurs.fr:5432/drone?sslmode=disable
DRONE_USER_CREATE=username:lx-admin,admin:true
DRONE_REGISTRATION_CLOSED=true
DRONE_LOGS_TEXT=true
DRONE_LOGS_PRETTY=true
DRONE_LOGS_DEBUG=true
@ -59,6 +60,7 @@ EOH
"traefik.enable=true",
"traefik.frontend.entryPoints=https,http",
"traefik.frontend.rule=Host:drone.deuxfleurs.fr",
"tricot drone.deuxfleurs.fr",
]
port = "web_port"
address_mode = "host"

View file

@ -26,31 +26,42 @@ curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compo
chmod +x /usr/local/bin/docker-compose
```
## Prepare the runner
## Install the runner
*This is our Nix runner version 2, previously we had another way to start Nix runners. This one has a proper way to handle concurrency, require less boilerplate, and should be safer and more idiomatic.*
Nix folder must be populated before launching any build.
```bash
docker run --rm -it -v /var/lib/drone/nix:/mnt nixpkgs/nix:nixos-21.05 cp -r /nix/{store,var} /mnt/
```
wget https://git.deuxfleurs.fr/Deuxfleurs/infrastructure/raw/branch/main/app/drone-ci/integration/nix.conf
wget https://git.deuxfleurs.fr/Deuxfleurs/infrastructure/raw/branch/main/app/drone-ci/integration/docker-compose.yml
This folder will grow over time and might need to be garbage collected.
As a rule of thumb, after running a full release of Garage, this folder will require 10GB.
Consider provisioning it with at least 20GB.
## Launch the runner
Because we use a shared nix folder, we set the number of concurrent builds to 1.
For more details and customizations, see `docker-compose.yml`.
```bash
DRONE_NAME=lheureduthe DRONE_OWNER=quentin DRONE_SECRET=xxx docker-compose up -d
# Edit the docker-compose.yml to adapt its variables to your needs,
# especially the capacitiy value and its name.
COMPOSE_PROJECT_NAME=drone DRONE_SECRET=xxx docker-compose up -d
```
That's all folks.
## Check if a given job is built by your runner
```bash
export URL=https://drone.deuxfleurs.fr
export REPO=Deuxfleurs/garage
export BUILD=1312
curl ${URL}/api/repos/${REPO}/builds/${BUILD} \
| jq -c '[.stages[] | { name: .name, machine: .machine }]'
```
It will give you the following result:
```json
[{"name":"default","machine":"1686a"},{"name":"release-linux-x86_64","machine":"vimaire"},{"name":"release-linux-i686","machine":"carcajou"},{"name":"release-linux-aarch64","machine":"caribou"},{"name":"release-linux-armv6l","machine":"cariacou"},{"name":"refresh-release-page","machine":null}]
```
## Random note
*This part might be deprecated!*
This setup is done mainly to allow nix builds with some cache.
To use the cache in Drone, you must set your repository as trusted.
The command line tool does not work (it says it successfully set your repository as trusted but it did nothing):

View file

@ -1,5 +1,14 @@
version: '3.4'
services:
nix-daemon:
image: nixpkgs/nix:nixos-22.05
restart: always
command: nix-daemon
privileged: true
volumes:
- "nix:/nix"
- "./nix.conf:/etc/nix/nix.conf:ro"
drone-runner:
image: drone/drone-runner-docker:latest
restart: always
@ -7,19 +16,30 @@ services:
- DRONE_RPC_PROTO=https
- DRONE_RPC_HOST=drone.deuxfleurs.fr
- DRONE_RPC_SECRET=${DRONE_SECRET}
- DRONE_RUNNER_CAPACITY=1
- DRONE_RUNNER_CAPACITY=3
- DRONE_DEBUG=true
- DRONE_LOGS_TRACE=true
- DRONE_RPC_DUMP_HTTP=true
- DRONE_RPC_DUMP_HTTP_BODY=true
- DRONE_RUNNER_NAME=${DRONE_NAME}
- DRONE_RUNNER_LABELS=nix:1
#- DRONE_RUNNER_VOLUMES=/var/lib/drone/nix:/nix
- DRONE_RUNNER_NAME=i_forgot_to_change_my_runner_name
- DRONE_RUNNER_LABELS=nix-daemon:1
# we should put "nix:/nix:ro but it is not supported by
# drone-runner-docker because the dependency envconfig does
# not support having two colons (:) in the same stanza.
# Without the RO flag (or using docker userns), build isolation
# is broken.
# https://discourse.drone.io/t/allow-mounting-a-host-volume-as-read-only/10071
# https://github.com/kelseyhightower/envconfig/pull/153
#
# A workaround for isolation is to configure docker with a userns,
# so even if the folder is writable to root, it is not to any non
# privileged docker daemon ran by drone!
- DRONE_RUNNER_VOLUMES=drone_nix:/nix
- DRONE_RUNNER_ENVIRON=NIX_REMOTE:daemon
ports:
- "3000:3000/tcp"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
- "/var/lib/drone/nix:/var/lib/drone/nix"
drone-gc:
image: drone/gc:latest
@ -30,3 +50,5 @@ services:
- GC_INTERVAL=10m
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
volumes:
nix:

View file

@ -0,0 +1,9 @@
substituters = https://cache.nixos.org https://nix.web.deuxfleurs.fr
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= nix.web.deuxfleurs.fr:eTGL6kvaQn6cDR/F9lDYUIP9nCVR/kkshYfLDJf1yKs=
max-jobs = auto
cores = 0
log-lines = 200
filter-syscalls = true
sandbox = true
keep-outputs = true
keep-derivations = true

View file

@ -21,8 +21,9 @@ compatibility_level = 2
#===
# TLS parameters
#===
smtpd_tls_cert_file=/etc/ssl/certs/postfix.crt
smtpd_tls_key_file=/etc/ssl/private/postfix.key
smtpd_tls_cert_file=/etc/ssl/postfix.crt
smtpd_tls_key_file=/etc/ssl/postfix.key
smtpd_tls_dh1024_param_file=auto
smtpd_use_tls=yes
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache

View file

@ -28,6 +28,12 @@ job "email" {
task "server" {
driver = "docker"
constraint {
attribute = "${attr.unique.hostname}"
operator = "="
value = "digitale"
}
config {
image = "superboum/amd64_dovecot:v6"
readonly_rootfs = false
@ -38,7 +44,7 @@ job "email" {
"secrets/ssl/certs:/etc/ssl/certs",
"secrets/ssl/private:/etc/ssl/private",
"secrets/conf/:/etc/dovecot/",
"/mnt/glusterfs/email/mail:/var/mail/",
"/mnt/ssd/mail:/var/mail/",
]
}
@ -143,12 +149,14 @@ job "email" {
# ----- secrets ------
template {
data = "{{ key \"secrets/email/dovecot/dovecot.crt\" }}"
# data = "{{ key \"secrets/email/dovecot/dovecot.crt\" }}"
data = "{{ with $d := key \"tricot/certs/imap.deuxfleurs.fr\" | parseJSON }}{{ $d.cert_pem }}{{ end }}"
destination = "secrets/ssl/certs/dovecot.crt"
perms = "400"
}
template {
data = "{{ key \"secrets/email/dovecot/dovecot.key\" }}"
# data = "{{ key \"secrets/email/dovecot/dovecot.key\" }}"
data = "{{ with $d := key \"tricot/certs/imap.deuxfleurs.fr\" | parseJSON }}{{ $d.key_pem }}{{ end }}"
destination = "secrets/ssl/private/dovecot.key"
perms = "400"
}
@ -253,8 +261,7 @@ job "email" {
command = "postfix"
args = [ "start-fg" ]
volumes = [
"secrets/ssl/certs:/etc/ssl/certs",
"secrets/ssl/private:/etc/ssl/private",
"secrets/ssl:/etc/ssl",
"secrets/postfix:/etc/postfix-conf",
"/dev/log:/dev/log"
]
@ -375,14 +382,16 @@ job "email" {
# --- secrets ---
template {
data = "{{ key \"secrets/email/postfix/postfix.crt\" }}"
destination = "secrets/ssl/certs/postfix.crt"
# data = "{{ key \"secrets/email/postfix/postfix.crt\" }}"
data = "{{ with $d := key \"tricot/certs/smtp.deuxfleurs.fr\" | parseJSON }}{{ $d.cert_pem }}{{ end }}"
destination = "secrets/ssl/postfix.crt"
perms = "400"
}
template {
data = "{{ key \"secrets/email/postfix/postfix.key\" }}"
destination = "secrets/ssl/private/postfix.key"
# data = "{{ key \"secrets/email/postfix/postfix.key\" }}"
data = "{{ with $d := key \"tricot/certs/smtp.deuxfleurs.fr\" | parseJSON }}{{ $d.key_pem }}{{ end }}"
destination = "secrets/ssl/postfix.key"
perms = "400"
}
}
@ -423,7 +432,8 @@ job "email" {
"alps",
"traefik.enable=true",
"traefik.frontend.entryPoints=https,http",
"traefik.frontend.rule=Host:alps.deuxfleurs.fr"
"traefik.frontend.rule=Host:alps.deuxfleurs.fr",
"tricot alps.deuxfleurs.fr",
]
check {
type = "tcp"
@ -477,7 +487,9 @@ job "email" {
"sogo",
"traefik.enable=true",
"traefik.frontend.entryPoints=https,http",
"traefik.frontend.rule=Host:www.sogo.deuxfleurs.fr,sogo.deuxfleurs.fr;PathPrefix:/"
"traefik.frontend.rule=Host:www.sogo.deuxfleurs.fr,sogo.deuxfleurs.fr;PathPrefix:/",
"tricot www.sogo.deuxfleurs.fr",
"tricot sogo.deuxfleurs.fr",
]
check {
type = "tcp"

View file

@ -0,0 +1 @@
USER AWS Acces Key ID

View file

@ -0,0 +1 @@
USER AWS Secret Access key

View file

@ -0,0 +1 @@
USER Restic backup password to encrypt data

View file

@ -0,0 +1 @@
USER Restic Repository URL, check op_guide/backup-minio to see the format

View file

@ -0,0 +1,60 @@
job "frontend" {
datacenters = ["dc1", "neptune"]
type = "service"
priority = 90
group "tricot" {
network {
port "http_port" { static = 80 }
port "https_port" { static = 443 }
}
task "server" {
driver = "docker"
config {
image = "lxpz/amd64_tricot:37"
network_mode = "host"
readonly_rootfs = true
ports = [ "http_port", "https_port" ]
}
resources {
cpu = 2000
memory = 500
}
restart {
interval = "30m"
attempts = 2
delay = "15s"
mode = "delay"
}
template {
data = <<EOH
TRICOT_NODE_NAME={{ env "attr.unique.hostname" }}
TRICOT_LETSENCRYPT_EMAIL=alex@adnab.me
TRICOT_ENABLE_COMPRESSION=true
RUST_LOG=tricot=debug
EOH
destination = "secrets/env"
env = true
}
service {
name = "tricot-http"
port = "http_port"
tags = [ "(diplonat (tcp_port 80))" ]
address_mode = "host"
}
service {
name = "tricot-https"
port = "https_port"
tags = [ "(diplonat (tcp_port 443))" ]
address_mode = "host"
}
}
}
}

View file

@ -8,16 +8,17 @@ replication_mode = "3"
rpc_bind_addr = "[::]:3901"
rpc_secret = "{{ key "secrets/garage/rpc_secret" | trimSpace }}"
consul_host = "consul.service.2.cluster.deuxfleurs.fr:8500"
consul_service_name = "garage-rpc-self-advertised"
bootstrap_peers = []
sled_cache_capacity = 536870912
sled_sync_interval_ms = 10000
[s3_api]
s3_region = "garage"
api_bind_addr = "[::]:3900"
root_domain = ".garage.deuxfleurs.fr"
[s3_web]
bind_addr = "[::]:3902"
root_domain = ".web.deuxfleurs.fr"
index = "index.html"
[admin]
api_bind_addr = "[::1]:3903"

View file

@ -25,7 +25,7 @@ job "garage" {
driver = "docker"
config {
advertise_ipv6_address = true
image = "dxflrs/amd64_garage:v0.4-rc2"
image = "dxflrs/amd64_garage:v0.7.1"
command = "/garage"
args = [ "server" ]
network_mode = "host"
@ -45,7 +45,7 @@ job "garage" {
}
resources {
memory = 1000
memory = 1500
cpu = 1000
}
@ -55,9 +55,8 @@ job "garage" {
service {
tags = [
"garage_api",
"traefik.enable=true",
"traefik.frontend.entryPoints=https,http",
"traefik.frontend.rule=Host:garage.deuxfleurs.fr"
"tricot garage.deuxfleurs.fr",
"tricot *.garage.deuxfleurs.fr",
]
port = 3900
address_mode = "driver"
@ -95,6 +94,32 @@ job "garage" {
}
}
service {
tags = [
"garage-web",
"tricot * 1",
"tricot-add-header Content-Security-Policy default-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' https://code.jquery.com/; frame-ancestors 'self'",
"tricot-add-header Strict-Transport-Security max-age=63072000; includeSubDomains; preload",
"tricot-add-header X-Frame-Options SAMEORIGIN",
"tricot-add-header X-XSS-Protection 1; mode=block",
]
port = 3902
address_mode = "driver"
name = "garage-web"
check {
type = "tcp"
port = 3902
address_mode = "driver"
interval = "60s"
timeout = "5s"
check_restart {
limit = 3
grace = "90s"
ignore_warnings = false
}
}
}
restart {
interval = "30m"
attempts = 10

View file

@ -59,7 +59,7 @@ listeners:
x_forwarded: false
resources:
- names: [client]
- names: [client, federation]
compress: true
- port: 8448
@ -83,6 +83,7 @@ listeners:
# Database configuration
database:
name: psycopg2
allow_unsafe_locale: false
args:
user: {{ key "secrets/chat/synapse/postgres_user" | trimSpace }}
password: {{ key "secrets/chat/synapse/postgres_pwd" | trimSpace }}

View file

@ -15,7 +15,7 @@ job "im" {
driver = "docker"
config {
image = "superboum/amd64_synapse:v47"
image = "superboum/amd64_synapse:v53"
network_mode = "host"
readonly_rootfs = true
ports = [ "client_port", "federation_port" ]
@ -95,11 +95,10 @@ job "im" {
address_mode = "host"
tags = [
"matrix",
"traefik.enable=true",
"traefik.frontend.entryPoints=https",
"traefik.frontend.rule=Host:im.deuxfleurs.fr;PathPrefix:/_matrix,/_synapse",
"traefik.frontend.headers.customResponseHeaders=Access-Control-Allow-Origin: *",
"traefik.frontend.priority=100"
"tricot im.deuxfleurs.fr/_matrix 100",
"tricot im.deuxfleurs.fr:443/_matrix 100",
"tricot im.deuxfleurs.fr/_synapse 100",
"tricot-add-header Access-Control-Allow-Origin *",
]
check {
type = "tcp"
@ -120,10 +119,8 @@ job "im" {
address_mode = "host"
tags = [
"matrix",
"traefik.enable=true",
"traefik.frontend.entryPoints=https",
"traefik.frontend.rule=Host:deuxfleurs.fr;PathPrefix:/_matrix",
"traefik.frontend.priority=100"
"tricot deuxfleurs.fr/_matrix 100",
"tricot deuxfleurs.fr:443/_matrix 100",
]
}
}
@ -133,7 +130,7 @@ job "im" {
driver = "docker"
config {
image = "superboum/amd64_synapse:v47"
image = "superboum/amd64_synapse:v53"
readonly_rootfs = true
command = "/usr/local/bin/matrix-s3-async"
work_dir = "/tmp"
@ -177,7 +174,7 @@ EOH
task "server" {
driver = "docker"
config {
image = "superboum/amd64_riotweb:v25"
image = "superboum/amd64_riotweb:v30"
ports = [ "web_port" ]
volumes = [
"secrets/config.json:/srv/http/config.json"
@ -196,10 +193,8 @@ EOH
service {
tags = [
"webstatic",
"traefik.enable=true",
"traefik.frontend.entryPoints=https",
"traefik.frontend.rule=Host:im.deuxfleurs.fr,riot.deuxfleurs.fr;PathPrefix:/",
"traefik.frontend.priority=10"
"tricot im.deuxfleurs.fr 10",
"tricot riot.deuxfleurs.fr 10",
]
port = "web_port"
address_mode = "host"

View file

@ -1,4 +1,4 @@
FROM debian:buster AS builder
FROM debian:bookworm AS builder
# unzip is required when executing the mvn package command
RUN apt-get update && \
@ -15,7 +15,7 @@ RUN mvn package -DskipTests -Dassembly.skipAssembly=false
RUN unzip target/jicofo-1.1-SNAPSHOT-archive.zip && \
mv jicofo-1.1-SNAPSHOT /srv/build
FROM debian:buster
FROM debian:bookworm
RUN apt-get update && \
apt-get install -y openjdk-11-jre-headless ca-certificates

View file

@ -3,6 +3,7 @@
update-ca-certificates -f
exec java \
-Dlog4j2.formatMsgNoLookups=true \
-Djdk.tls.ephemeralDHKeySize=2048 \
-Djava.util.logging.config.file=/usr/share/jicofo/lib/logging.properties \
-Dconfig.file=/etc/jitsi/jicofo.conf \

View file

@ -1,8 +1,8 @@
FROM debian:buster AS builder
FROM debian:bookworm AS builder
RUN apt-get update && \
apt-get install -y curl && \
curl -sL https://deb.nodesource.com/setup_14.x | bash - && \
curl -sL https://deb.nodesource.com/setup_16.x | bash - && \
apt-get install -y git nodejs make git unzip
ARG MEET_TAG
@ -12,7 +12,7 @@ WORKDIR jitsi-meet
RUN npm install && \
make
FROM debian:buster
FROM debian:bookworm
COPY --from=builder /jitsi-meet /srv/jitsi-meet
RUN apt-get update && \

View file

@ -1,31 +0,0 @@
From b327e580ab83110cdb52bc1d11687a096b8fc1df Mon Sep 17 00:00:00 2001
From: Quentin Dufour <quentin@dufour.io>
Date: Mon, 1 Feb 2021 07:16:50 +0100
Subject: [PATCH] Disable legacy parameters
---
jvb/src/main/kotlin/org/jitsi/videobridge/Main.kt | 8 --------
1 file changed, 8 deletions(-)
diff --git a/jvb/src/main/kotlin/org/jitsi/videobridge/Main.kt b/jvb/src/main/kotlin/org/jitsi/videobridge/Main.kt
index df71f480..8f0ef9a5 100644
--- a/jvb/src/main/kotlin/org/jitsi/videobridge/Main.kt
+++ b/jvb/src/main/kotlin/org/jitsi/videobridge/Main.kt
@@ -62,14 +62,6 @@ fun main(args: Array<String>) {
// to be passed.
System.setProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.JavaUtilLog")
- // Before initializing the application programming interfaces (APIs) of
- // Jitsi Videobridge, set any System properties which they use and which
- // may be specified by the command-line arguments.
- System.setProperty(
- Videobridge.REST_API_PNAME,
- cmdLine.getOptionValue("--apis").contains(Videobridge.REST_API).toString()
- )
-
// Reload the Typesafe config used by ice4j, because the original was initialized before the new system
// properties were set.
JitsiConfig.reloadNewConfig()
--
2.25.1

View file

@ -0,0 +1,40 @@
From 01507442620e5a57624c921b508eac7d572440d0 Mon Sep 17 00:00:00 2001
From: Quentin Dufour <quentin@deuxfleurs.fr>
Date: Tue, 25 Jan 2022 14:46:22 +0100
Subject: [PATCH] Remove deprecated argument
---
.../main/kotlin/org/jitsi/videobridge/Main.kt | 17 -----------------
1 file changed, 17 deletions(-)
diff --git a/jvb/src/main/kotlin/org/jitsi/videobridge/Main.kt b/jvb/src/main/kotlin/org/jitsi/videobridge/Main.kt
index 4f6cb78..3db00f2 100644
--- a/jvb/src/main/kotlin/org/jitsi/videobridge/Main.kt
+++ b/jvb/src/main/kotlin/org/jitsi/videobridge/Main.kt
@@ -52,23 +52,6 @@ import org.jitsi.videobridge.websocket.singleton as webSocketServiceSingleton
fun main(args: Array<String>) {
val logger = LoggerImpl("org.jitsi.videobridge.Main")
- // We only support command line arguments for backward compatibility. The --apis options is the last one supported,
- // and it is only used to enable/disable the REST API (XMPP is only controlled through the config files).
- // TODO: fully remove support for --apis
- CmdLine().apply {
- parse(args)
- getOptionValue("--apis")?.let {
- logger.warn(
- "A deprecated command line argument (--apis) is present. Please use the config file to control the " +
- "REST API instead (see rest.md). Support for --apis will be removed in a future version."
- )
- System.setProperty(
- Videobridge.REST_API_PNAME,
- it.contains(Videobridge.REST_API).toString()
- )
- }
- }
-
setupMetaconfigLogger()
setSystemPropertyDefaults()
--
2.33.1

View file

@ -1,4 +1,4 @@
FROM debian:buster AS builder
FROM debian:bookworm AS builder
RUN apt-get update && \
apt-get install -y git unzip maven openjdk-11-jdk-headless
@ -8,15 +8,15 @@ RUN git clone --depth 1 --branch ${JVB_TAG} https://github.com/jitsi/jitsi-video
WORKDIR jitsi-videobridge
COPY *.patch .
RUN git apply 0001-Disable-legacy-parameters.patch
RUN git apply 0001-Remove-deprecated-argument.patch
RUN mvn package -DskipTests
RUN unzip jvb/target/jitsi-videobridge*.zip && \
mv jitsi-videobridge-*-SNAPSHOT build
FROM debian:buster
FROM debian:bookworm
RUN apt-get update && \
apt-get install -y openjdk-11-jre-headless curl
apt-get install -y openjdk-11-jre-headless curl iproute2
COPY --from=builder /jitsi-videobridge/build /usr/share/jvb
COPY jvb_run /usr/local/bin/jvb_run

View file

@ -12,6 +12,7 @@ fi
echo "NAT config: ${JITSI_NAT_LOCAL_IP} -> ${JITSI_NAT_PUBLIC_IP}"
exec java \
-Dlog4j2.formatMsgNoLookups=true \
-Djdk.tls.ephemeralDHKeySize=2048 \
-Djava.util.logging.config.file=/usr/share/jvb/lib/logging.properties \
-Dconfig.file=/etc/jitsi/videobridge.conf \

View file

@ -1,4 +1,4 @@
FROM debian:buster as builder
FROM debian:bookworm as builder
RUN apt-get update && \
apt-get install -y git unzip
@ -6,7 +6,7 @@ RUN apt-get update && \
ARG MEET_TAG
RUN git clone --depth 1 --branch ${MEET_TAG} https://github.com/jitsi/jitsi-meet/
FROM debian:buster
FROM debian:bookworm
ARG PROSODY_VERSION
RUN apt-get update && \

View file

@ -1,5 +1,5 @@
# some doc: https://www.nginx.com/resources/wiki/start/topics/examples/full/
error_log /dev/stderr;
error_log /dev/stderr info;
events {}
@ -39,8 +39,10 @@ http {
# inspired by https://raw.githubusercontent.com/jitsi/docker-jitsi-meet/master/web/rootfs/defaults/meet.conf
server {
listen 0.0.0.0:{{ env "NOMAD_PORT_https_port" }} ssl http2 default_server;
listen [::]:{{ env "NOMAD_PORT_https_port" }} ssl http2 default_server;
#listen 0.0.0.0:{{ env "NOMAD_PORT_https_port" }} ssl http2 default_server;
#listen [::]:{{ env "NOMAD_PORT_https_port" }} ssl http2 default_server;
listen 0.0.0.0:{{ env "NOMAD_PORT_https_port" }} default_server;
listen [::]:{{ env "NOMAD_PORT_https_port" }} default_server;
client_max_body_size 0;
server_name _;
@ -48,8 +50,8 @@ http {
ssi on;
ssi_types application/x-javascript application/javascript;
ssl_certificate /etc/nginx/jitsi.crt;
ssl_certificate_key /etc/nginx/jitsi.key;
#ssl_certificate /etc/nginx/jitsi.crt;
#ssl_certificate_key /etc/nginx/jitsi.key;
root /srv/jitsi-meet;
index index.html;
error_page 404 /static/404.html;
@ -90,7 +92,7 @@ http {
add_header 'Access-Control-Allow-Origin' '*';
proxy_pass http://{{ env "NOMAD_ADDR_bosh_port" }}/http-bind;
proxy_set_header X-Forwarded-For \$remote_addr;
proxy_set_header Host \$http_host;
#proxy_set_header Host \$http_host;
}
# not used yet VVV

View file

@ -21,7 +21,7 @@ job "jitsi" {
task "xmpp" {
driver = "docker"
config {
image = "superboum/amd64_jitsi_xmpp:v9"
image = "superboum/amd64_jitsi_xmpp:v10"
ports = [ "bosh_port", "xmpp_port" ]
network_mode = "host"
volumes = [
@ -102,7 +102,7 @@ EOF
task "front" {
driver = "docker"
config {
image = "superboum/amd64_jitsi_meet:v4"
image = "superboum/amd64_jitsi_meet:v5"
network_mode = "host"
ports = [ "https_port" ]
volumes = [
@ -144,7 +144,8 @@ EOF
"traefik.enable=true",
"traefik.frontend.entryPoints=https",
"traefik.frontend.rule=Host:jitsi.deuxfleurs.fr;PathPrefix:/",
"traefik.protocol=https"
"traefik.protocol=https",
"tricot jitsi.deuxfleurs.fr",
]
port = "https_port"
address_mode = "host"
@ -166,7 +167,7 @@ EOF
task "jicofo" {
driver = "docker"
config {
image = "superboum/amd64_jitsi_conference_focus:v7"
image = "superboum/amd64_jitsi_conference_focus:v9"
network_mode = "host"
volumes = [
"secrets/certs/jitsi.crt:/usr/local/share/ca-certificates/jitsi.crt",
@ -200,7 +201,7 @@ EOF
task "videobridge" {
driver = "docker"
config {
image = "superboum/amd64_jitsi_videobridge:v17"
image = "superboum/amd64_jitsi_videobridge:v20"
network_mode = "host"
ports = [ "video_port" ]
ulimit {

View file

@ -0,0 +1,44 @@
# Bridge accounts on various services
[rocketchat]
[rocketchat.dravedev]
Server = "https://rocketchat.drave.quebec:443"
Login = "{{ key "secrets/matterbridge/rocketchat.drave.quebec_user" | trimSpace }}"
Password = "{{ key "secrets/matterbridge/rocketchat.drave.quebec_pass" | trimSpace }}"
PrefixMessagesWithNick=false
RemoteNickFormat="{NICK}"
[matrix]
[matrix.deuxfleurs]
Server = "https://im.deuxfleurs.fr"
Login = "{{ key "secrets/matterbridge/im.deuxfleurs.fr_user" | trimSpace }}"
Password = "{{ key "secrets/matterbridge/im.deuxfleurs.fr_pass" | trimSpace }}"
PrefixMessagesWithNick=true
RemoteNickFormat="<{NICK}> "
[discord]
[discord.la-console]
Token = "{{ key "secrets/matterbridge/discord.com_token" | trimSpace }}"
Server = "872244032443678730"
RemoteNickFormat="{NICK}"
PrefixMessagesWithNick=false
AutoWebhooks = true
# Rooms we are bridging
[[gateway]]
name = "rfid"
enable = true
[[gateway.inout]]
account = "rocketchat.dravedev"
channel = "rfid"
[[gateway.inout]]
account = "matrix.deuxfleurs"
channel = "#rfid:deuxfleurs.fr"
[[gateway.inout]]
account = "discord.la-console"
channel = "rfid"

View file

@ -0,0 +1,40 @@
job "matterbridge" {
datacenters = ["dc1"]
type = "service"
priority = 90
constraint {
attribute = "${attr.cpu.arch}"
value = "amd64"
}
group "main" {
count = 1
task "bridge" {
driver = "docker"
config {
image = "42wim/matterbridge:1.23"
readonly_rootfs = true
volumes = [
"secrets/matterbridge.toml:/etc/matterbridge/matterbridge.toml"
]
}
resources {
memory = 200
}
template {
data = file("../config/matterbridge.toml")
destination = "secrets/matterbridge.toml"
}
restart {
attempts = 10
delay = "30s"
}
}
}
}

View file

@ -41,7 +41,8 @@ EOH
"platoo",
"traefik.enable=true",
"traefik.frontend.entryPoints=https",
"traefik.frontend.rule=Host:platoo.deuxfleurs.fr;PathPrefix:/"
"traefik.frontend.rule=Host:platoo.deuxfleurs.fr;PathPrefix:/",
"tricot platoo.deuxfleurs.fr",
]
port = "web_port"
address_mode = "host"

View file

@ -1,4 +1,4 @@
FROM rust:1.54.0-slim-bullseye as builder
FROM rust:1.58.1-slim-bullseye as builder
RUN apt-get update && \
apt-get install -y \
@ -10,6 +10,7 @@ RUN apt-get update && \
libpq-dev \
gettext \
git \
python \
curl \
gcc \
make \
@ -25,11 +26,11 @@ WORKDIR /opt/plume
RUN git checkout ${VERSION}
WORKDIR /opt/plume/script
RUN chmod a+x ./wasm-deps.sh && sleep 1 && ./wasm-deps.sh
RUN chmod a+x ./wasm-deps.sh && ./wasm-deps.sh
WORKDIR /opt/plume
RUN cargo install wasm-pack
RUN chmod a+x ./script/plume-front.sh && sleep 1 && ./script/plume-front.sh
RUN chmod a+x ./script/plume-front.sh && ./script/plume-front.sh
RUN cargo install --path ./ --force --no-default-features --features postgres
RUN cargo install --path plume-cli --force --no-default-features --features postgres
RUN cargo clean
@ -40,13 +41,14 @@ FROM debian:bullseye-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
libpq5 \
libssl1.1
libssl1.1 \
rclone \
fuse
WORKDIR /app
COPY --from=builder /opt/plume /app
COPY --from=builder /usr/local/cargo/bin/plm /usr/local/bin/
COPY --from=builder /usr/local/cargo/bin/plume /usr/local/bin/
COPY plm-start /usr/local/bin/
CMD ["plm-start"]
CMD ["plume"]

View file

@ -1,9 +0,0 @@
#!/bin/bash
until plm migration run;
do sleep 2;
done
plm search init
plm instance new --domain "$DOMAIN_NAME" --name "$INSTANCE_NAME" --private
plume

View file

@ -1,4 +1,4 @@
job "plume" {
job "plume-blog" {
datacenters = ["dc1"]
type = "service"
@ -15,16 +15,22 @@ job "plume" {
}
task "plume" {
constraint {
attribute = "${attr.unique.hostname}"
operator = "="
value = "digitale"
}
driver = "docker"
config {
image = "superboum/plume:v4"
image = "superboum/plume:v8"
network_mode = "host"
ports = [ "web_port" ]
#command = "cat"
#args = [ "/dev/stdout" ]
volumes = [
"/mnt/glusterfs/plume/media:/app/static/media",
"/mnt/glusterfs/plume/search:/app/search_index"
"/mnt/ssd/plume/search_index:/app/search_index",
"/mnt/ssd/plume/media:/app/static/media"
]
}
@ -46,6 +52,7 @@ job "plume" {
"traefik.enable=true",
"traefik.frontend.entryPoints=https,http",
"traefik.frontend.rule=Host:plume.deuxfleurs.fr",
"tricot plume.deuxfleurs.fr",
]
port = "web_port"
address_mode = "host"
@ -63,6 +70,12 @@ job "plume" {
}
}
}
restart {
interval = "30m"
attempts = 20
delay = "15s"
mode = "delay"
}
}
}
}

View file

@ -0,0 +1 @@
USER Backup AWS access key ID

View file

@ -0,0 +1 @@
USER Backup AWS secret access key

View file

@ -0,0 +1 @@
USER Restic password to encrypt backups

View file

@ -0,0 +1 @@
USER Restic repository, eg. s3:https://s3.garage.tld

View file

@ -1,4 +1,4 @@
FROM golang:1.13-buster AS builder
FROM golang:1.19.0-bullseye AS builder
ARG STOLON_VERSION
WORKDIR /stolon
@ -9,7 +9,7 @@ COPY 0001-Add-max-rate-to-pg_basebackup.patch .
RUN git apply 0001-Add-max-rate-to-pg_basebackup.patch
RUN make && chmod +x /stolon/bin/*
FROM postgres:13.3-buster
FROM postgres:14.5-bullseye
COPY --from=builder /stolon/bin /usr/local/bin
USER postgres
ENTRYPOINT []

View file

@ -93,6 +93,12 @@ job "postgres13.3" {
"--pg-su-password", "${PG_SU_PWD}",
"--pg-repl-username", "${PG_REPL_USER}",
"--pg-repl-password", "${PG_REPL_PWD}",
/*
The postgres daemon accepts 0.0.0.0, ::, and * here but not Stolon.
Otherwise you will have the following error and your cluster will be broken (no replication)
WARN cmd/keeper.go:1979 provided --pg-listen-address "*": is not an ip address but a hostname. This will be advertized to the other components and may have undefined behaviors if resolved differently by other hosts
WARN cmd/keeper.go:1984 cannot resolve provided --pg-listen-address "*": lookup *: no such host
*/
"--pg-listen-address", "${attr.unique.network.ip-address}",
"--pg-port", "${NOMAD_PORT_psql_port}",
"--pg-bin-path", "/usr/lib/postgresql/13/bin/"

View file

@ -1,3 +1,3 @@
python-consul==1.1.0
python-ldap==3.3.1
python-ldap==3.4.0
passlib==1.7.4

15
app/shell.nix Normal file
View file

@ -0,0 +1,15 @@
{
pkgs ? import <nixpkgs> {}
}:
with pkgs; mkShell {
nativeBuildInputs = [
nomad
docker-compose
python39Packages.pip
python39Packages.ldap
python39Packages.consul
python39Packages.passlib
];
}

View file

@ -1,59 +0,0 @@
InsecureSkipVerify = true
defaultEntryPoints = ["http", "https"]
[entryPoints]
[entryPoints.admin]
address = ":8082"
[entryPoints.http]
address = ":80"
[entryPoints.http.redirect]
entryPoint = "https"
[entryPoints.https]
address = ":443"
compress = true
[entryPoints.https.tls]
minVersion = "VersionTLS12"
cipherSuites = [
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"
]
[ping]
entrypoint = "admin"
[retry]
[acme]
email = "quentin@dufour.io"
storage = "traefik/acme/account"
entryPoint = "https"
onHostRule = true
[acme.httpChallenge]
entryPoint = "http"
[api]
entryPoint = "admin"
dashboard = true
[consul]
endpoint = "172.17.0.1:8500"
watch = true
prefix = "traefik"
[consulCatalog]
endpoint = "172.17.0.1:8500"
prefix = "traefik"
domain = "web.deuxfleurs.fr"
exposedByDefault = false
[metrics]
[metrics.prometheus]
# -- below is for traefik 1.7 see https://doc.traefik.io/traefik/v1.7/configuration/metrics/
entryPoint = "admin"

View file

@ -1,72 +0,0 @@
job "frontend" {
datacenters = ["dc1"]
type = "service"
priority = 80
group "traefik" {
network {
port "http_port" { static = 80 }
port "https_port" { static = 443 }
port "admin_port" { static = 8082 }
}
task "server" {
driver = "docker"
config {
image = "amd64/traefik:1.7.28"
readonly_rootfs = true
network_mode = "host"
volumes = [
"secrets/traefik.toml:/etc/traefik/traefik.toml",
]
ports = [ "http_port", "https_port", "admin_port" ]
}
resources {
memory = 265
}
template {
data = file("../config/traefik.toml")
destination = "secrets/traefik.toml"
}
service {
name = "traefik-http"
port = "http_port"
tags = [ "(diplonat (tcp_port 80))" ]
address_mode = "host"
}
service {
name = "traefik-https"
port = "https_port"
tags = [ "(diplonat (tcp_port 443))" ]
address_mode = "host"
}
service {
name = "traefik-admin"
port = "admin_port"
address_mode = "host"
check {
type = "http"
protocol = "http"
port = 8082
address_mode = "driver"
path = "/ping"
interval = "60s"
timeout = "5s"
check_restart {
limit = 3
grace = "90s"
ignore_warnings = false
}
}
}
}
}
}

3
op_guide/README.md Normal file
View file

@ -0,0 +1,3 @@
All documents from our operations guide have been moved to <https://guide.deuxfleurs.fr/operations/> (or in Git repository `guide.deuxfleurs.fr`).
Tous les documents de notre guide des opérations ont été déplacés sur <https://guide.deuxfleurs.fr/operations/> (ou dans le dépôt Git `guide.deuxfleurs.fr`).

View file

@ -1,26 +0,0 @@
## 1. Create a LDAP user and assign a password for your service
Go to guichet.deuxfleurs.fr
1. Everything takes place in `ou=services,ou=users,dc=deuxfleurs,dc=fr`
2. Create a new user, like `johny`
3. Generate a random password with `openssl rand -base64 32`
4. Hash it with `slappasswd`
5. Add a `userpassword` entry with the hash
This step can also be done using the automated tool `secretmgr.py` in the app folder.
## 2. Connect to postgres with the admin users
```bash
# 1. Launch ssh tunnel given in the README
# 2. Make sure you have postregsql client installed locally
psql -h localhost -U postgres -W postgres
```
## 3. Create the binded users with LDAP in postgres + the database
```sql
CREATE USER johny;
CREATE DATABASE amazingapp OWNER johny;
```

View file

@ -1,72 +0,0 @@
Spawn container:
```bash
docker run -t -i superboum/arm32v7_postgres:v6
# OR
docker run -t -i superboum/amd64_postgres:v1
```
Init with:
```
stolonctl \
--cluster-name pissenlit \
--store-backend=consul \
--store-endpoints http://consul.service.2.cluster.deuxfleurs.fr:8500 \
init \
'{ "initMode": "new", "pgHBA": [ "host all postgres all md5", "host replication replicator all md5", "host all all all ldap ldapserver=bottin.service.2.cluster.deuxfleurs.fr ldapbasedn=\"ou=users,dc=deuxfleurs, dc=fr\" ldapbinddn=\"<bind_dn>\" ldapbindpasswd=\"<bind_pwd>\" ldapsearchattribute=\"cn\"" ] }'
```
Then set appropriate permission on host:
```
chown -R 102:102 /mnt/storage/postgres/
```
(102 is the id of the postgres user used in Docker)
It might be improved by staying with root, then chmoding in an entrypoint and finally switching to user 102 before executing user's command.
Moreover it would enable the usage of the user namespace that shift the UIDs.
## Upgrading the cluster
To retreive the current stolon config:
```
stolonctl spec --cluster-name pissenlit --store-backend consul --store-endpoints http://consul.service.2.cluster.deuxfleurs.fr:8500
```
The important part for the LDAP:
```
{
"pgHBA": [
"host all postgres all md5",
"host replication replicator all md5",
"host all all all ldap ldapserver=bottin.service.2.cluster.deuxfleurs.fr ldapbasedn=\"ou=users,dc=deuxfleurs,dc=fr\" ldapbinddn=\"cn=admin,dc=deuxfleurs,dc=fr\" ldapbindpasswd=\"<REDACTED>\" ldapsearchattribute=\"cn\""
]
}
```
Once a patch is writen:
```
stolonctl --cluster-name pissenlit --store-backend consul --store-endpoints http://consul.service.2.cluster.deuxfleurs.fr:8500 update --patch -f /tmp/patch.json
```
## Log
- 2020-12-18 Activate pg\_rewind in stolon
```
stolonctl --cluster-name pissenlit --store-backend consul --store-endpoints http://consul.service.2.cluster.deuxfleurs.fr:8500 update --patch '{ "usePgrewind" : true }'
```
- 2021-03-14 Increase proxy timeout to cope with consul latency spikes
```
stolonctl --cluster-name pissenlit --store-backend consul --store-endpoints http://consul.service.2.cluster.deuxfleurs.fr:8500 update --patch '{ "proxyTimeout" : "120s" }'
```

View file

@ -1,60 +0,0 @@
# How to setup NextCloud
## First setup
It's complicated.
First, create a service user `nextcloud` and a database `nextcloud` it owns. Also create a Garage access key and bucket `nextcloud` it is allowed to use.
Fill in the following Consul keys with actual values:
```
secrets/nextcloud/db_user
secrets/nextcloud/db_pass
secrets/nextcloud/garage_access_key
secrets/nextcloud/garage_secret_key
```
Create the following Consul keys with empty values:
```
secrets/nextcloud/instance_id
secrets/nextcloud/password_salt
secrets/nextcloud/secret
```
Start the nextcloud.hcl nomad service. Enter the container and call `occ maintenance:install` with the correct database parameters as user `www-data`.
A possibility: call the admin user `nextcloud` and give it the same password as the `nextcloud` service user.
Cat the newly generated `config.php` file and copy the instance id, password salt, and secret from there to Consul
(they were generated by the install script and we want to keep them).
Restart the Nextcloud Nomad server.
You should now be able to log in to Nextcloud using the admin user (`nextcloud` if you called it that).
Go to the apps settings and enable desired apps.
## Configure LDAP login
LDAP login has to be configured from the admin interface. First, enable the LDAP authentification application.
Go to settings > LDAP/AD integration. Enter the following parameters:
- ldap server: `bottin2.service.2.cluster.deuxfleurs.fr`
- bind user: `cn=nextcloud,ou=services,ou=users,dc=deuxfleurs,dc=fr`
- bind password: password of the nextcloud service user
- base DN for users: `ou=users,dc=deuxfleurs,dc=fr`
- check "manually enter LDAP filters"
- in the users tab, edit LDAP query and set it to `(&(|(objectclass=inetOrgPerson))(|(memberof=cn=nextcloud,ou=groups,dc=deuxfleurs,dc=fr)))`
- in the login attributes tab, edit LDAP query and set it to `(&(&(|(objectclass=inetOrgPerson))(|(memberof=cn=nextcloud,ou=groups,dc=deuxfleurs,dc=fr)))(|(|(mailPrimaryAddress=%uid)(mail=%uid))(|(cn=%uid))))`
- in the groups tab, edit the LDAP query and set it to `(|(objectclass=groupOfNames))`
- in the advanced tab, enter the "directory setting" section and check/modify the following:
- user display name field: `displayname`
- base user tree: `ou=users,dc=deuxfleurs,dc=fr`
- user search attribute: `cn`
- groupe display name field: `displayname`
- **base group tree**: `ou=groups,dc=deuxfleurs,dc=fr`
- group search attribute: `cn`
That should be it. Go to the login attributes tab and enter a username (which should have been added to the nextcloud group) to check that nextcloud is able to find it and allows it for login.

View file

@ -1,20 +0,0 @@
## Creating a new Plume user
1. Bind nomad on your machine with SSH (check the README file at the root of this repo)
2. Go to http://127.0.0.1:4646
3. Select `plume` -> click `exec` button (top right)
4. Select `plume` on the left panel
5. Press `enter` to get a bash shell
6. Run:
```bash
plm users new \
--username alice \
--display-name Alice \
--bio Just an internet user \
--email alice@example.com \
--password s3cr3t
```
That's all folks, now you can use your new account at https://plume.deuxfleurs.fr

View file

@ -1,45 +0,0 @@
Le 20 janvier free a changé mon IP, un peu comme partout en France.
Ça concerne l'IPv4 et le préfixe IPv6.
Ici le bon vieux Bortzmoinsbien qui tweet : https://twitter.com/bortzmeyer/status/1351434290916155394
Max a update tout de suite l'IPv4 mais avec un TTL de 4h le temps de propagation est grand.
J'ai réduit les entrées sur les IP à 300 secondes, soit 5 minutes, le minimum chez Gandi, à voir si c'est une bonne idée.
Reste à update les IPv6, moins critiques pour le front facing mais utilisées pour le signaling en interne...
## Le fameux signaling
Ça pose un gros problème avec Nomad (et en moindre mesure avec Consul).
En effet, Nomad utilise l'IPv6 pour communiquer, il faut donc changer les IPs de tous les noeuds.
Problème ! On peut pas faire la migration au fur et à mesure car, changeant d'IP, les noeuds ne seront plus en mesure de communiquer.
On n'a pas envie de supprimer le cluster et d'en créer un nouveau car ça voudrait dire tout redéployer ce qui est long également (tous les fichiers HCL pour Nomad, tout le KV pour consul).
On ne peut pas non plus la faire à la bourrin en stoppant tous les cluster, changer son IP, puis redémarrer.
Enfin si, Consul accepte mais pas Nomad, qui lui va chercher à communiquer avec les anciennes IP et n'arrivera jamais à un consensus.
Au passage j'en ai profité pour changer le nom des noeuds car la dernière fois, Nomad n'avait PAS DU TOUT apprécié qu'un noeud ayant le même nom change d'IP. Ceci dit, si on utilise de facto le `peers.json` c'est peut être pas problématique. À tester.
Du coup, après moult réflexions, la silver bullet c'est la fonction outage recovery de nomad (consul l'a aussi au besoin).
Elle est ici : https://learn.hashicorp.com/tutorials/consul/recovery-outage
En gros, il faut arrêter tous les nodes.
Ensuite créer un fichier à ce path : `/var/lib/nomad/server/raft/peers.json`
Ne vous laissez pas perturber par le fichier `peers.info` à côté, il ne faut pas le toucher.
Après la grande question c'est de savoir si le cluster est en Raft v2 ou Raft v3.
Bon ben nous on était en Raft v2. Si vous vous trompez, au redémarrage Nomad va crasher avec une sale erreur :
```
nomad: failed to start Raft: error="recovery failed to parse peers.json: json: cannot unmarshal string into Go value of type raft.configEntry"
```
(je me suis trompé bien sûr).
Voilà, après il ne vous reste plus qu'à redémarrer et suivre les logs, cherchez bien la ligne où il dit qu'il a trouvé le peers.json.
## Les trucs à pas oublier
- Reconfigurer le backend KV de traefik (à voir à utiliser des DNS plutôt du coup)
- Reconfigurer l'IPv4 publique annoncée à Jitsi
## Ce qui reste à faire
- Mettre à jour les entrées DNS IPv6, ce qui devrait créer :
- digitale.machine.deuxfleurs.fr
- datura.machine.deuxfleurs.fr
- drosera.machine.deuxfleurs.fr
- Mettre à jour l'instance garage sur io

View file

@ -1,15 +0,0 @@
```
curl http://127.0.0.1:8500/v1/kv/traefik/acme/account/object?raw > traefik.gzip
gunzip -c traefik.gzip > traefik.json
cat traefik.json | jq '.DomainsCertificate.Certs[] | .Certificate.Domain, .Domains.Main'
# "alps.deuxfleurs.fr"
# "alps.deuxfleurs.fr"
# "cloud.deuxfleurs.fr"
# "cloud.deuxfleurs.fr"
# chaque NDD doit apparaitre 2x à la suite sinon fix comme suit
cat traefik.json | jq > traefik-new.json
vim traefik-new.json
# enlever les certifs corrompus, traefik les renouvellera automatiquement au démarrage
gzip -c traefik-new.json > traefik-new.gzip
curl --request PUT --data-binary @traefik-new.gzip http://127.0.0.1:8500/v1/kv/traefik/acme/account/object
```

View file

@ -1,93 +0,0 @@
How to update Matrix?
=====================
## 1. Build the new containers
Often, I update Riot Web and Synapse at the same time.
* Open `app/docker-compose.yml` and locate `riot` (the Element Web service) and `synapse` (the Matrix Synapse server). There are two things you need to do for each service:
* Set the `VERSION` argument to the target service version (e.g. `1.26.0` for Synapse). This argument is then used to template the Dockerfile.
The `VERSION` value should match a github release, the link to the corresponding release page is put as a comment next to the variable in the compose file;
* Tag the image with a new incremented version tag. For example: `superboum/amd64_riotweb:v17` will become `superboum/amd64_riotweb:v18`.
We use the docker hub to store our images. So, if you are not `superboum` you must change the name with your own handle, eg. `john/amd64_riotweb:v18`. This requires that you registered an account (named `john`) on https://hub.docker.com.
So, from now we expect you have:
* changed the `VERSION` value and `image` name/tag of `riot`
* changed the `VERSION` value and `image` name/tag of `synapse`
From the `/app` folder, you can now simply build and push the new images:
```bash
docker-compose build riot synapse
```
And then send them to the docker hub:
```
docker-compose push riot synapse
```
Don't forget to commit and push your changes before doing anything else!
## 2. Deploy the new containers
Now, we will edit the deployment file `app/im/deploy/im.hcl`.
Find where the image is defined in the file, for example Element-web will look like that:
```hcl
group "riotweb" {
count = 1
task "server" {
driver = "docker"
config {
image = "superboum/amd64_riotweb:v17"
port_map {
web_port = 8043
}
```
And replace the `image =` entry with its new version created above.
Do the same thing for the `synapse` service.
Now, you need a way to access the cluster to deploy this file.
To do this, you must bind nomad on your machine through a SSH tunnel.
Check the end of [the parent `README.md`](../README.md) to do it.
If you have access to the Nomad web UI when entering http://127.0.0.1:4646
you are ready to go.
You must have installed the Nomad command line tool on your machine (also explained in [the parent `README.md`](../README.md)).
Now, on your machine and from the `app/im/deploy` folder, you must be able to run:
```
nomad plan im.hcl
```
Check that the proposed diff corresponds to what you have in mind.
If it seems OK, just copy paste the `nomad job run ... im.hcl` command proposed as part of the output of the `nomad plan` command.
From now, it will take around ~2 minutes to deploy the new images.
You can follow the deployment from the Nomad UI.
Bear in mind that, once the deployment is done on Nomad, you may still need to wait some minutes that Traefik refreshes its configuration.
If everythings worked as intended, you can commit and push your deployment file.
If something went wrong, you must rollback your deployment.
1. First, find a working deployment with [nomad job history](https://www.nomadproject.io/docs/commands/job/history)
2. Revert to this deployment with [nomad job revert](https://www.nomadproject.io/docs/commands/job/revert)
Now, if the deployment failed, you should probably investigate what went wrong offline.
I built a test stack with docker-compose in `app/<service>/integration` that should help you out (for now, test suites are only written for plume and jitsi).

View file

@ -4,9 +4,12 @@
For each machine, **one by one** do:
- Check that cluster is healthy
- Check gluster
- `sudo gluster peer status`
- `sudo gluster volume status all` (check Online Col, only `Y` must appear)
- Check garage
- check that all nodes are online `docker exec -ti xxx /garage status`
- check that tables are in sync `docker exec -ti 63a4d7ecd795 /garage repair --yes tables`
- check garage logs
- no unknown errors or resync should be in progress
- the following line must appear `INFO garage_util::background > Worker exited: Repair worker`
- Check that Nomad is healthy
- `nomad server members`
- `nomad node status`
@ -17,5 +20,5 @@ For each machine, **one by one** do:
- Run `nomad node drain -enable -force -self`
- Reboot
- Run `nomad node drain -self -disable`
- Check that cluster is healthy
- Check that cluster is healthy (basically the whole first point)

View file

@ -14,6 +14,12 @@
- role: network
tags: net
# UNSAFE!! This section configures glusterfs. Once done, don't run it ever again as it may break stuff.
# - role: storage
# tags: sto
- hosts: extra_nodes
serial: 1
roles:
- role: common
tags: base
- role: users
tags: account
- role: network
tags: net

View file

@ -7,7 +7,7 @@ cluster_nodes:
ipv4: 192.168.0.2
gatewayv4: 192.168.0.254
ipv6: 2a01:e0a:260:b5b0::2
gatewayv6: 2a01:e34:ec5c:dbe0::1
gatewayv6: 2a01:e0a:260:b5b0::1
interface: eno1
dns_1: 212.27.40.240
dns_2: 212.27.40.241
@ -39,6 +39,8 @@ cluster_nodes:
dns_2: 212.27.40.241
ansible_python_interpreter: python3
extra_nodes:
hosts:
io:
ansible_host: io.machine.deuxfleurs.fr
ansible_port: 22
@ -46,7 +48,7 @@ cluster_nodes:
ipv4: 192.168.1.57
gatewayv4: 192.168.1.1
ipv6: 2a01:e0a:5e4:1d0::57
gatewayv6: 2a01:e0a:5e4:1d0::1
gatewayv6: 2a01:e0a:5e4:1d0::2
interface: enp2s0
dns_1: 9.9.9.10
dns_2: 149.112.112.10

View file

@ -43,6 +43,15 @@
- ethtool
- pciutils
- pv
- zstd
- miniupnpc
- rsync
- ncdu
- smartmontools
- ioping
- lm-sensors
- netcat
- sysstat
state: present
- name: "Passwordless sudo"

View file

@ -1,6 +1,6 @@
- name: "Set consul version"
set_fact:
consul_version: 1.9.1
consul_version: 1.11.4
- name: "Download and install Consul for x86_64"
unarchive:

View file

@ -10,12 +10,12 @@
-A INPUT -p tcp --dport 22 -j ACCEPT
# Diplonat needs everything open to communicate with IGD with the router
-A INPUT -s 192.168.1.254 -j ACCEPT
-A INPUT -s 192.168.0.254 -j ACCEPT
# Cluster
{% for selected_host in groups['cluster_nodes'] %}
-A INPUT -s {{ hostvars[selected_host]['ipv4'] }} -j ACCEPT
{% endfor %}
-A INPUT -s 192.168.0.2 -j ACCEPT
-A INPUT -s 192.168.0.3 -j ACCEPT
-A INPUT -s 192.168.0.4 -j ACCEPT
# Local
-A INPUT -i docker0 -j ACCEPT

View file

@ -16,9 +16,9 @@
-A INPUT -p tcp --dport 22 -j ACCEPT
# Cluster
{% for selected_host in groups['cluster_nodes'] %}
-A INPUT -s {{ hostvars[selected_host]['ipv6'] }} -j ACCEPT
{% endfor %}
-A INPUT -s 2a01:e0a:260:b5b0::2 -j ACCEPT
-A INPUT -s 2a01:e0a:260:b5b0::3 -j ACCEPT
-A INPUT -s 2a01:e0a:260:b5b0::4 -j ACCEPT
# Local
-A INPUT -i docker0 -j ACCEPT
@ -27,7 +27,7 @@
# Who is part of our trusted net?
# Max@Bruxelles
-A DEUXFLEURS-TRUSTED-NET -s 2a02:1811:3606:4800::0/64 -j DEUXFLEURS-TRUSTED-PORT
-A DEUXFLEURS-TRUSTED-NET -s 2a02:1811:3612:b300::0/64 -j DEUXFLEURS-TRUSTED-PORT
# Max@Suresnes
-A DEUXFLEURS-TRUSTED-NET -s 2a01:e0a:183:7be2::0/64 -j DEUXFLEURS-TRUSTED-PORT
# Max@OVH
@ -36,10 +36,20 @@
-A DEUXFLEURS-TRUSTED-NET -s 2a01:e0a:5e4:1d0::0/64 -j DEUXFLEURS-TRUSTED-PORT
# ADRN@Gandi
-A DEUXFLEURS-TRUSTED-NET -s 2001:4b98:dc0:41:216:3eff:fe9b:1afb/128 -j DEUXFLEURS-TRUSTED-PORT
# ADRN@Lille
-A DEUXFLEURS-TRUSTED-NET -s 2a01:e0a:e4:2dd0::0/64 -j DEUXFLEURS-TRUSTED-PORT
# Quentin@Rennes
-A DEUXFLEURS-TRUSTED-NET -s 2a01:e35:2fdc:dbe0::0/64 -j DEUXFLEURS-TRUSTED-PORT
# Erwan@Rennes
-A DEUXFLEURS-TRUSTED-NET -s 2a01:e0a:260:b5b0::0/64 -j DEUXFLEURS-TRUSTED-PORT
# LX@Orsay
-A DEUXFLEURS-TRUSTED-NET -s 2a06:a004:3025:1::0/64 -j DEUXFLEURS-TRUSTED-PORT
-A DEUXFLEURS-TRUSTED-NET -s 2a06:a003:515d:1::0/64 -j DEUXFLEURS-TRUSTED-PORT
-A DEUXFLEURS-TRUSTED-NET -s 2001:910:1204:1::0/64 -j DEUXFLEURS-TRUSTED-PORT
# Zorun@Nantes
-A DEUXFLEURS-TRUSTED-NET -s 2a00:5881:4008::/56 -j DEUXFLEURS-TRUSTED-PORT
# Quentin@Lyon
-A DEUXFLEURS-TRUSTED-NET -s 2a01:e0a:28f:5e60::/64 -j DEUXFLEURS-TRUSTED-PORT
# Source address is not trusted
-A DEUXFLEURS-TRUSTED-NET -j RETURN

View file

@ -1,6 +1,6 @@
- name: "Set nomad version"
set_fact:
nomad_version: 1.0.2
nomad_version: 1.2.6
- name: "Download and install Nomad for x86_64"
unarchive:

View file

@ -1,3 +0,0 @@
---
- name: umount gluster
shell: umount --force --lazy /mnt/glusterfs ; true

View file

@ -1,72 +0,0 @@
- name: "Add GlusterFS Repo Key"
apt_key:
url: https://download.gluster.org/pub/gluster/glusterfs/5/rsa.pub
state: present
- name: "Add GlusterFS official repository"
apt_repository:
repo: "deb [arch=amd64] https://download.gluster.org/pub/gluster/glusterfs/5/LATEST/Debian/buster/amd64/apt buster main"
state: present
filename: gluster
- name: "Install GlusterFS"
apt:
name:
- glusterfs-server
- glusterfs-client
state: present
- name: "Ensure Gluster Daemon started and enabled"
service:
name: glusterd
enabled: yes
state: started
- name: "Create directory for GlusterFS bricks"
file: path=/mnt/storage/glusterfs/brick1 recurse=yes state=directory
- name: "Create GlusterFS volumes"
gluster_volume:
state: present
name: donnees
bricks: /mnt/storage/glusterfs/brick1/g1
#rebalance: yes
redundancies: 1
disperses: 3
#replicas: 3
force: yes
options:
client.event-threads: "8"
server.event-threads: "8"
performance.stat-prefetch: "on"
nfs.disable: "on"
features.cache-invalidation: "on"
performance.client-io-threads: "on"
config.transport: tcp
performance.quick-read: "on"
performance.io-cache: "on"
nfs.export-volumes: "off"
cluster.lookup-optimize: "on"
cluster: "{% for selected_host in groups['cluster_nodes'] %}{{ hostvars[selected_host]['ipv4'] }}{{ ',' if not loop.last else '' }}{% endfor %}"
run_once: true
- name: "Create mountpoint"
file: path=/mnt/glusterfs recurse=yes state=directory
- name: "Flush handlers (umount glusterfs and restart ganesha)"
meta: flush_handlers
- name: "Add fstab entry"
tags: gluster-fstab
mount:
path: /mnt/glusterfs
src: "{{ ipv4 }}:/donnees"
fstype: glusterfs
opts: "defaults,_netdev,noauto,x-systemd.automount"
state: present
- name: Mount everything
command: mount -a
args:
warn: no