Crée un serveur de partage de fichiers

contact


Si vous souhaitez me remonter des informations, ne pas hésiter

contact

Intro


Transfer.sh permet de partager des fichiers via la commande sous windows (Invoke-RestMethod) et linux (curl) et bien d'autres (wget, http). Il est possible d'utiliser le service en ligne, ou de mettre en place un serveur au sein de votre infrastructure en local ou en ligne.
L'intérêt de le mettre en place dans votre entreprise ou sur un serveur dédié permet d'éviter le partage des fichiers sensibles sur un site externe

Voici le site de partage en question :
https://transfer.sh/

Préparation en vue d'une mise en production


Vous avez deux possibilités

  • Compiler l'application pour en faire un exécutable que vous pourrez lancer sur un serveur sans GO, mais il faudra recompiler à chaque mise à jour
  • L'utiliser avec GO qui le compilera aussi pour pouvoir l'exécuter (le langage créé par Google)

je vais détailler les deux possibilités en commençant par l'utilisation directement avec GO, puis dans la continuité, comment le compiler afin qu'il fonctionne sans prérequis sur tous les serveurs ayant la même architecture que le serveur qui la compilait

Nous allons utiliser une debian 10 et les outils seront en armv6 (GO ainsi que le programme compilé)
Si vous n'utilisez pas une architecture Arm sur votre serveur, il faudra donc choisir les outils adaptés au processeur utilisé
Les commandes seront identiques même si les architectures sont différentes (processeurs)

Mise à jour Debian 10

Commencez par installer debian 10

Mettez-le à jour

apt update && apt upgrade

&& sert à exécuter la commande suivante, si la première s'est bien déroulée

Installation des prérequis

Voici les paquets prérequis depuis une debian sous LXC

apt install curl nano git unzip gcc 
  • Curl va servir pour le téléchargement de GO et du script transfert.sh.
  • Git va servir à la mise en place de transfert.sh
  • unzip pour extraire l'archive téléchargée
  • gcc va permettre de compiler l'outil

Mise en place de GO

Allez sur le site de GO
Dans la liste un peu plus bas, télécharger la version qui correspond à votre architecture

Attention, n'installez pas le paquet GO présent dans la distribution (si vous avez les sources d'APT par défaut), car il est obsolète et ne fonctionne pas avec le script de mise en place de transfersh.

Dans mon cas je prends le paquet suivant : go1.16.3.linux-armv6l.tar.gz, cliquez sur le lien du paquet souhaité puis copiez le lien présent dans this link en haut de la page apparue

Allez sur votre serveur et entrez la commande suivante :

curl -SLO https://golang.org/dl/go1.16.3.linux-armv6l.tar.gz

Voici l'explication des options CURL

  • S affichent les erreurs
  • L suit les redirections pour télécharger le fichier final
  • O garde le nom proposé lors du téléchargement

Entrez la commande suivante pour extraire l'archive dans le dossier des applications

tar -C /usr/local -xzf go1.16.3.linux-armv6l.tar.gz

Vous allez ajouter l'exécutable dans les variables d'environnement du système, ça permettra de faire appel à la commande go

Entrez la commande suivante pour faire les modifications au niveau de /etc/profile (Attention sur Ubuntu la modification se fait dans /etc/environment, faite le à la main car il n'y a qu'une ligne)

sed -i "$(grep -num2 PATH /etc/profile |cut -d':' -f1 | sed -z "s+\n+,+")s+PATH=\"+PATH=\"/usr/local/go/bin:+" /etc/profile

Explication de la commande:
commençons par $(grep -num2 PATH /etc/profile |cut -d':' -f1 | sed -z "s+\n+,+")

  • $() permet d'implémenter le résultat de la commande qui est insérée entre les parenthèses dans une autre commande
  • grep -num2 PATH /etc/profile récupère les deux premières occurrences PATH dans /etc/profile
  • cut -d':' -f1 On récupère la première colonne (-f1), le délimiteur est :
  • sed -z "s+\n+,+ On remplace les retours à la ligne par une virgule. Ce qui donne les deux premières lignes contenant l'occurrence PATH affichées à la suite avec une virgule entre les deux (5,7 par exemple)
    Continuons avec la seconde partie de la commande sed -i "$(grep -num2 PATH /etc/profile |cut -d':' -f1 | sed -z "s+\n+,+")s+PATH=\"+PATH=\"/usr/local/go/bin:+" /etc/profile
  • sed -i modifie le fichier concerné directement
  • s+ fait une comparaison RegEx, le + remplace le / pour éviter les erreurs avec les / de la ligne à remplacer
  • PATH=\"+PATH=\"/usr/local/go/bin:+" remplace les occurrences PATH=\" par PATH=\"/usr/local/go/bin:
  • /etc/profile modifie le fichier indiqué

Cette commande va donc remplacer les 2 premières occurrences PATH=" par PATH="/usr/local/go/bin:. Si vous avez peur de vous tromper, faites les modifications à la main.

Une fois les modifications effectuées, redémarrer votre OS si vous avez modifié /etc/profile

reboot

Pour que la commande go ne soit appliquée qu'à un profil. Entrez la commande suivante

echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.profile

Il faudra fermer et réouvrir votre session pour que cela prenne effet.

Après le redémarrage ou la réouverture de session, vérifier si cela fonctionne correctement

go version

Vous devriez avoir la version actuelle de GO

Téléchargement de Transfersh et essais de fonctionnement

Il y à deux façons de faire

Manuel

Allez sur le git de transfertsh pour télécharger l'archive
Cliquez sur l'icône vert (code), faite un clic droite sur download zip et copier l'URL

Sur votre serveur, tapez la commande copiée

curl -SLO https://github.com/dutchcoders/transfer.sh/archive/refs/heads/master.zip

Automatique

git clone https://github.com/dutchcoders/transfer.sh.git

dézipper l'archive

unzip master.zip

Puis entrez dans le dossier dézippé

cd transfer.sh-master/

Trouvez l'IP de la machine si vous ne l'avez pas déjà avec la commande

ip a

Testez le fonctionnement en root. La première fois ça peut prendre un certain temps, car il va télécharger tous les prérequis et compiler le programme

go run main.go --provider=local --listener :8080 --basedir=/tmp/ 

Explication de la commande

  • --provider=local indique que le stockage est local
  • --listener :8080 écoute sur le port 8080 ouvert à tous, si vous avez un reverse proxy sur le même serveur indiqué localhost:8080, pour que seul le serveur en local puisse y accéder
  • --temp-path=/tmp/ --basedir=/tmp/ le répertoire de stockage temporaire et permanent sont /tmp/

Une fois la compilation terminée vous aurez le message suivant : listening on port: :8080

Allez sur le navigateur de votre poste de travail et entrez l'adresse du serveur

IP:8080

Si la page de transfersh apparait, alors c'est que cela fonctionne.

Retournez sur le serveur et faites CTRL + C de votre clavier pour quitter le programme

Compiler le programme

Depuis le dossier dans lequel vous avez lancé le programme, entrez la commande suivante

go build -o transfersh main.go

Dès que vous reprenez la main sur le terminal, lancez la commande suivante pour tester le programme

./transfersh --provider=local --listener :8080 --basedir=/tmp/

Dès que le message suivant apparait listening on port: :8080, connectez-vous à IP:8080 avec un navigateur
Si tout fonctionne, retournez au terminal du serveur et faites un CTRL+C pour quitter le programme

Vous pouvez copier transfersh sur un autre serveur qui à une architecture identique au serveur sur lequel vous l'avez compilé pour la mise en production.

Utilisation et essais de la version compilée de transfersh

Options disponibles pour l'application

Pour avoir toutes les options disponibles, il suffit de faire un ./transfersh --help en étant dans le dossier de l'application

Comme vous l'avez remarqué il y a beaucoup de possibilités, concentrons-nous sur les plus élémentaires
--purge-days value Nombre de jours de rétention des fichiers
--purge-interval value Nombre d'heures de rétention des fichiers
--max-upload-size value Taille maximum des fichiers envoyés (en kilooctet)
--log value Chemin du fichier log
--http-auth-user value Nom d'utilisateur pour la demande d'authentification
--http-auth-pass value Mot de passe pour la demande d'authentification
--ip-whitelist value Liste des ip autorisés a se connecter séparer avec des virgule (,)
--ip-blacklist value Liste des ip non autorisés à se connecter séparer avec des virgule (,)

Attention ci-dessous, vous devez choisir soit l'utilisation de certificats autosignés pour un accès local en général, soit la mise en place de certificats fournis par lets encrypt dans le cas d'un accès depuis l'extérieur (serveur dédié).

1 Exécution de transfersh avec certificat autosigné (en mode local)

Pour lancer le serveur en HTTPS, il faudra générer un certificat autosigné avec les commandes suivantes

mkdir /etc/ssl/transfersh/
openssl genrsa -out /etc/ssl/transfersh/transfersh.key 2048                                                                                                   
openssl req -new -key /etc/ssl/transfersh/transfersh.key -out /etc/ssl/transfersh/transfersh.csr                                                              
openssl x509 -req -days 365 -in /etc/ssl/transfersh/transfersh.csr -signkey /etc/ssl/transfersh/transfersh.key -out /etc/ssl/transfersh/transfersh.crt
  • Concernant openssl req -new -key /etc/ssl/transfersh/transfersh.key -out /etc/ssl/transfersh/transfersh.csr si l'accès au site se fait via l'IP, laissez toutes les questions par défaut sinon remplissez les valeurs, la plus importante Common Name (e.g. server FQDN or YOUR name), remplissez la avec votre nom de domaine
  • chmod 400 permet de restreindre les droits au minimum sur la clé privée**
  • -days 365 indique que l'on génère un certificat valide pour 1 an, vous pouvez augmenter ou réduire la durée, cela vous obligera à générer de nouveau un certificat à la fin de la validité de celui-ci
    Puis lancer le serveur avec les options SSL
./transfersh --provider=local --basedir=/tmp/ --tls-listener-only --tls-listener 0.0.0.0:443   --tls-private-key /etc/ssl/transfersh/transfersh.key --tls-cert-file /etc/ssl/transfersh/transfersh.crt

Ou avec Go

go run main.go --provider=local --basedir=/tmp/ --tls-listener-only --tls-listener 0.0.0.0:443   --tls-private-key /etc/ssl/transfersh/transfersh.key --tls-cert-file /etc/ssl/transfersh/transfersh.crt
  • --tls-listener-only Force le SSL, pas de possibilité d'être connecté sans chiffrement
  • --tls-listener 0.0.0.0:443 on s'ouvre à tous sur le port 443 (port https par défaut)
  • --tls-private-key et --tls-cert-file indique les clés du certificat privé et public

2 Exécution de transfersh avec Lets encrypt (Accessible depuis l'extérieur)

Commencez par installer certbot

apt install certbot

Faites votre enregistrement DNS vers le serveur ou vous utiliserez transfersh

Entrez la commande suivante

certbot certonly --standalone  --agree-tos -d SOUSDOMAINE.VOTREDOMAINE --register-unsafely-without-email

Voici l'explication de la commande

  • certonly ne crée que le certificat
  • --standalone lance un serveur temporaire le temps de l'authentification avec les serveurs lets encrypt
  • --agree-tos accepte le contrat d'utilisation
  • -d SOUSDOMAINE.VOTREDOMAINE vous indiquez le domaine qui va être certifié
  • --register-unsafely-without-email On ne veut pas fournir d'email
  • SOUSDOMAINE.VOTREDOMAINE remplacer cela par votre domaine ou sous domaine

Puis exécuter transfersh

./transfersh --provider=local --basedir=/tmp/ --tls-listener-only --tls-listener 0.0.0.0:443   --tls-private-key /etc/letsencrypt/live/SOUSDOMAINE.VOTREDOMAINE/privkey.pem --tls-cert-file /etc/letsencrypt/live/SOUSDOMAINE.VOTREDOMAINE/fullchain.pem

Ou avec Go

go run main.go --provider=local --basedir=/tmp/ --tls-listener-only --tls-listener 0.0.0.0:443   --tls-private-key /etc/letsencrypt/live/SOUSDOMAINE.VOTREDOMAINE/privkey.pem --tls-cert-file /etc/letsencrypt/live/SOUSDOMAINE.VOTREDOMAINE/fullchain.pem

Vous voilà avec transfertsh en mode HTTPS avec des certificats fournis par lets encrypt

Le renouvèlement des certificats est automatique, il se fait via un cron qui est dans /etc/cron.d/certbot qui s'exécute tout les 12h

Mise en production


Je vais vous expliquer comment le mettre en production, nous allons voir les éléments suivants

  • Copier l'exécutable compilé au bon endroit
  • Créer un service systemd pour l'exécution au démarrage
  • Attribution d'un utilisateur pour l'utilisation du service (non root)

Pour ceux qui feraient une mise en production à l'extérieur, je recommande une sécurisation plus stricte via GEOIP et fail2ban et d'utiliser les options du logiciel --ip-whitelist et --ip-blacklist
Geoip pour restreindre l'accès à certains pays
Fail2ban pour bannir les erreurs 404 liées à la recherche d'URL par brute force. Mais aussi avec portsentry pour bannir les scans de port du serveur

Copie de l'exécutable

Copiez l'exécutable dans le dossier /usr/local/bin/ du serveur de production

cp transfersh /usr/local/bin/

/usr/local/bin est le répertoire dédié aux applications qui ne sont pas liées au système

Création d'un service avec systemd

Avant de créer le service, vous allez créer un utilisateur qui servira au lancement du service, le but étant de donner un minimum de droits pour éviter les risques de compromettre le serveur

Entrez la commande suivante

adduser --system --no-create-home --shell /bin/bash --disabled-password --disabled-login transfersh

Créer le fichier log pour le service

touch /var/log/transfersh.log

Vous modifiez les droits du fichier pour que l'utilisateur transfersh puisse écrire dedans

chown transfersh /var/log/transfersh.log

Maintenant, vous allez créer le service systemd

 nano /etc/systemd/system/transfersh.service

1 En extérieur, SSL avec lets encrypt

[Unit]
Description=Transfersh permet l'envoi de fichiers en ligne de commande
After=network-online.target

[Service]
Type=simple
User=transfersh
UMask=077

ExecStart=/usr/local/bin/transfersh --provider=local --basedir=/tmp/ --tls-listener-only --tls-listener 0.0.0.0:443  --tls-private-key /etc/ssl/transfersh/privkey1.pem --tls-cert-file /etc/ssl/transfersh/fullchain1.pem --purge-days 6 --max-upload-size 1000000 --log /var/log/transfersh.log                                                               

Restart=on-failure

# Configures the time to wait before service is stopped forcefully.
TimeoutStopSec=300

[Install]
WantedBy=multi-user.target

After=network-online.target démarre après le service indiqué, dans notre cas après le lancement de l'interface ethernet
UMask=077 Indique le masque des fichiers créés, ça sera 700 car on indique le masque d'annulation, donc 777 - 077 = 700
ExecStart= La commande à exécuter
Restart=on-failure redémarre en ça d'échec du lancement
TimeoutStopSec=300 temps avant arrêt forcé du service (cas de l'arrêt du serveur par exemple)
WantedBy=multi-user.target fonctionne que lors du démarrage du système en mode multiuser

Donner les droits au fichier de certificat

Nous allons déplacer les certificats lets encrypt pour permettre à l'utilisateur transfersh d'y accéder

cp /etc/letsencrypt/archive/dependancenum.fr/{fullchain1.pem,privkey1.pem} /etc/ssl/transfersh/

Puis nous modifions les droits de l'utilisateur pour que transfersh puisse accéder aux clés

chown -R transfersh /etc/ssl/transfersh

Vous allez attribuer les droits d'écriture et lecture et d'exécution au dossier transfersh

chmod 700 /etc/ssl/transfersh/

Nous donnons les droits minimums à la clé privés

chmod 400 /etc/ssl/transfersh/privkey1.pem
Automatiser le renouvèlement

Ajouter la ligne suivante dans /etc/cron.d/certbot, cette ligne va vérifier le checksum des certificats public (celui de lets encrypt et celui copié), et s'il y a une différence, c'est que le certificat a changé. Ainsi il copie les nouveaux et modifier les droits utilisateurs sur les certificats copiés

5 */12 * * * root if [ $(md5sum /etc/letsencrypt/archive/dependancenum.fr/fullchain1.pem|awk -F " " '{print $1}') = $(md5sum /etc/ssl/transfersh/fullchain1.pem|awk -F " " '{print $1}') ]; then echo "certificats vérifié et à jour" >> /var/log/transfersh.log; else systemctl stop transfersh && cp -f /etc/letsencrypt/archive/dependancenum.fr/{fullchain1.pem,privkey1.pem} /etc/ssl/transfersh/ && chown -R transfersh /etc/ssl/transfersh && chmod 400 /etc/ssl/transfersh/{fullchain1.pem,privkey1.pem} && systemctl start transfersh && echo "certificat remplacé" >> /var/log/transfersh.log; fi

2 En intérieur, avec ou sans SSL sur le port 80 ou 443 en modifiant la ligne à exécuter

[Unit]
Description=Transfersh permet l'envoi de fichiers en ligne de commande
After=network-online.target

[Service]
Type=simple
User=transfersh
UMask=077

ExecStart=/usr/local/bin/transfersh --provider=local --basedir=/tmp/ --listener 0.0.0.0:80 --purge-days 6 --max-upload-size 1000000 --log /var/log/transfersh.log                                                               
OU
ExecStart=/usr/local/bin/transfersh --provider=local --basedir=/tmp/ --tls-listener-only --tls-listener 0.0.0.0:443   --tls-private-key /etc/ssl/transfersh/transfersh.key --tls-cert-file /etc/ssl/transfersh/transfersh.crt

Restart=on-failure
TimeoutStopSec=300

[Install]
WantedBy=multi-user.target     
Modifier les droits des certificats
chown -R transfersh:root /etc/ssl/transfersh/
chmod 700 /etc/ssl/transfersh/
chmod 400 /etc/ssl/transfersh/transfersh.key
  • chown transfersh Va permettre à l'utilisateur transfersh d'utiliser les clés pour la mise en production
  • -R veut dire récursif, il va donc modifier les droits des sous-dossiers du dossier indiqué
  • chmod 700 Permet à transfersh de pouvoir écrire, lire et exécuter dans le dossier
  • chmod 400 donne le droit de lecture à transfersh uniquement
Automatiser le renouvèlement de certificat

Pour automatiser le renouvellement du certificat, nous allons vérifier la validité de celui-ci, si il n'est plus valide alors il crée un nouveau certificat

Ajouter cela à /etc/crontab

5 */12 * * * root if true | openssl s_client -connect 127.0.0.1:443 2>/dev/null |  openssl x509 -noout -checkend 0; then echo "Certificat encore valide" >> /var/log/transfersh.log; else echo "Certificat expiré, actualisation du certificat le $(date '+%d/%m/%Y %r') pour 1 an" >> /var/log/transfersh.log &&  systemctl stop transfersh && rm /etc/ssl/transfersh/transfersh.crt && openssl x509 -req -days 365 -in /etc/ssl/transfersh/transfersh.csr -signkey /etc/ssl/transfersh/transfersh.key -out /etc/ssl/transfersh/transfersh.crt && systemctl start transfersh; fi

Ouverture de port CAPABILITIES

Comme vous allez lancer votre service avec un utilisateur système sur le port 443 ou 80. Il va falloir l'autoriser à écouter sur l'un de ces ports

Seul root a le droit d'écouter sur les ports jusqu'à 1024, il va donc falloir modifier les capabilities sur votre exécutable pour que l'utilisateur transfersh ait le droit d'écoute sans être root. C'est une alternative à setuid. Au lieu de donner les droits root de façon générale, il les donne sur une partie des accès à UN programme. Dans notre cas l'ouverture de ports inférieur à 1024 qui nécessite les droits root

Vous allez donner les droits à l'application transfersh uniquement sur la partie ouverture de port, cela à tous les utilisateurs (cela peut être appliqué à un utilisateur si besoin)

setcap cap_net_bind_service=pe `which transfersh`

On affiche les droits capabilities appliqués à transfersh, vous devriez avoir la ligne suivante : /usr/local/bin/transfersh = cap_net_bind_service+ep

getcap `which transfersh`

Activez le service

systemctl enable transfersh

On lance le service

systemctl start transfersh

Commande pour envoyer des fichiers


Sous Windows voici la commande à entrer

Attention pour utiliser l'option -SkipCertificateCheck il faut avoir la version 6.0.0 de Powershell minimum, car elle a été implémentée que depuis cette version comme l'indique microsoft

Pour connaitre la version de Powershell que vous avez, ouvrez Powershell et entrer la commande suivante

$PsVersionTable

Allez sur ce lien pour mettre à jour votre version de Powershell, télécharger la version stable powershell

Dans le cas d'un certificat autosigné, entrez la commande suivante pour uploader votre fichier

Invoke-RestMethod -SkipCertificateCheck -Method PUT -InFile .\fichier -Uri https://IPSRV/fichier

Si vous avez mis en place un certificat lets encrypt, Vous utiliserez la commande suivante

Invoke-RestMethod -Method PUT -InFile .\fichier -Uri https://IPSRV/fichier

Sous Linux voici la commande avec un certificat délivré par lets encrypt

curl --upload-file ./hello.txt https://IPSRV/hello.txt

Sous Linux voici la commande avec un certificat autosigné

curl -k --upload-file ./hello.txt https://IPSRV/hello.txt
  • -k permet de ne pas faire attention aux erreurs liées au certificat

Les autres options sont disponibles sur le Github

SOURCES

https://gist.github.com/r4dian/47c60f43fcfc68059cbe
https://github.com/dutchcoders/transfer.sh
https://www.digitalocean.com/community/tutorials/how-to-install-go-on-debian-10
https://golang.org/doc/install?download=go1.16.3.linux-armv6l.tar.gz
https://transfer.sh/
https://connect.ed-diamond.com/GNU-Linux-Magazine/GLMF-164/Les-capabilities-sous-Linux
https://sametmax.com/ecouter-sur-le-port-80-sans-etre-root/
https://doc.ubuntu-fr.org/creer_un_service_avec_systemd
https://ubuntuplace.info/questions/389535/how-do-you-add-cap-sys-admin-permissions-to-user-in-centos-7
https://pelicanux.net/blog/2018/07/19/durcissement-systeme-avec-les-capabilities-linux/
https://stackoverflow.com/questions/1956732/is-it-possible-to-configure-linux-capabilities-per-user
https://doc.ubuntu-fr.org/acl
https://ubuntuplace.info/questions/313612/script-to-check-if-ssl-certificate-is-valid
https://github.com/PowerShell/PowerShell
https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-restmethod?view=powershell-7.1
https://unix.stackexchange.com/questions/60750/does-curl-have-a-no-check-certificate-option-like-wget