From a3da268eadd4244e2ebeb480d4d91a852c4257c0 Mon Sep 17 00:00:00 2001 From: Miraty Date: Thu, 22 Dec 2022 01:44:57 +0100 Subject: [PATCH] ht: subdomain and subpath on shared domain --- config.ini | 9 +++- db/schema.sql | 9 ++-- fn/ht.php | 53 ++++++++++++------- pages.php | 32 ++++++----- pg-act/auth/unregister.php | 15 +----- pg-act/ht/{add-http-dns.php => add-dns.php} | 6 +-- .../ht/{add-http-onion.php => add-onion.php} | 4 +- pg-act/ht/add-subdomain.php | 19 +++++++ pg-act/ht/add-subpath.php | 19 +++++++ pg-act/ht/del-http-dns.php | 8 --- pg-act/ht/del-http-onion.php | 8 --- pg-act/ht/del.php | 11 ++++ pg-view/ht/{add-http-dns.php => add-dns.php} | 4 +- .../ht/{add-http-onion.php => add-onion.php} | 4 +- pg-view/ht/add-subdomain.php | 18 +++++++ pg-view/ht/add-subpath.php | 18 +++++++ pg-view/ht/del-http-dns.php | 16 ------ pg-view/ht/del-http-onion.php | 16 ------ pg-view/ht/del.php | 23 ++++++++ pg-view/ht/index.php | 15 ++++-- router.php | 1 - 21 files changed, 194 insertions(+), 114 deletions(-) rename pg-act/ht/{add-http-dns.php => add-dns.php} (93%) rename pg-act/ht/{add-http-onion.php => add-onion.php} (93%) create mode 100644 pg-act/ht/add-subdomain.php create mode 100644 pg-act/ht/add-subpath.php delete mode 100644 pg-act/ht/del-http-dns.php delete mode 100644 pg-act/ht/del-http-onion.php create mode 100644 pg-act/ht/del.php rename pg-view/ht/{add-http-dns.php => add-dns.php} (86%) rename pg-view/ht/{add-http-onion.php => add-onion.php} (72%) create mode 100644 pg-view/ht/add-subdomain.php create mode 100644 pg-view/ht/add-subpath.php delete mode 100644 pg-view/ht/del-http-dns.php delete mode 100644 pg-view/ht/del-http-onion.php create mode 100644 pg-view/ht/del.php diff --git a/config.ini b/config.ini index ce84e74..cc94ba0 100644 --- a/config.ini +++ b/config.ini @@ -31,6 +31,13 @@ enabled = true ; Path were user's sites will be stored ht_path = "/srv/niver/ht" + +subpath_domain = "ht.niver.test" +subpath_path = "/srv/niver/subpath" + +subdomain_domain = "ht.niver.test" +subdomain_path = "/srv/niver/subdomain" + ; Nginx configuration directory nginx_config_path = "/srv/niver/nginx" nginx_reload_cmd = "/usr/bin/systemctl reload nginx" @@ -58,7 +65,7 @@ ipv4_address = "127.0.0.1" sftp_pub = "/etc/sftpgo/ed25519.pub" sftp_fp = "/etc/sftpgo/ed25519.fp" sftp_asciiart = "/etc/sftpgo/ed25519.asciiart" -sftp_domain = "ht.niver.test" +sftp_domain = "sftp.niver.test" public_sftp_port = 2022 ; Will be used in configuration files diff --git a/db/schema.sql b/db/schema.sql index a50e99c..d99c4b8 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -39,11 +39,12 @@ CREATE TABLE IF NOT EXISTS "zones" ( CREATE TABLE IF NOT EXISTS "sites" ( "username" TEXT NOT NULL, "site_dir" TEXT NOT NULL, - "domain" TEXT NOT NULL UNIQUE, - "domain_type" TEXT NOT NULL, - "protocol" TEXT NOT NULL, + "address" TEXT NOT NULL, + "type" TEXT NOT NULL, "creation_date" TEXT NOT NULL, - PRIMARY KEY("domain"), + UNIQUE("address", "type"), + UNIQUE("username", "site_dir", "type"), + PRIMARY KEY("address", "type"), FOREIGN KEY("username") REFERENCES "users"("id") ); COMMIT; diff --git a/fn/ht.php b/fn/ht.php index 6c6d9c3..9a90044 100644 --- a/fn/ht.php +++ b/fn/ht.php @@ -21,24 +21,22 @@ function listFsDirs($username) { return $dirs; } -function addSite($username, $siteDir, $domain, $domainType, $protocol) { +function addSite($username, $siteDir, $address, $type) { insert('sites', [ 'username' => $username, 'site_dir' => $siteDir, - 'domain' => $domain, - 'domain_type' => $domainType, - 'protocol' => $protocol, + 'address' => $address, + 'type' => $type, 'creation_date' => date('Y-m-d H:i:s'), ]); } -function dirsStatuses($domainType, $protocol) { +function dirsStatuses($type) { if (isset($_SESSION['id']) !== true) return []; $dbDirs = query('select', 'sites', [ 'username' => $_SESSION['id'], - 'domain_type' => $domainType, - 'protocol' => $protocol, + 'type' => $type, ], 'site_dir'); $dirs = []; foreach (listFsDirs($_SESSION['id']) as $fsDir) @@ -46,9 +44,31 @@ function dirsStatuses($domainType, $protocol) { return $dirs; } -function htDeleteSite($dir, $domainType, $protocol) { +function htDeleteSite($address, $type) { + match ($type) { + 'onion', 'dns' => htDeleteDedicatedSite($address, $type), + 'subpath', 'subdomain' => htDeleteSubSite($address, $type) + }; +} - if ($domainType === 'onion') { +function htDeleteSubSite($address, $type) { + if (unlink(CONF['ht'][$type . '_path'] . '/' . $address) !== true) + output(500, 'Unable to delete symlink.'); + + query('delete', 'sites', [ + 'username' => $_SESSION['id'], + 'type' => $type, + 'address' => $address, + ]); +} + +function htDeleteDedicatedSite($address, $type) { + $dir = query('select', 'sites', [ + 'address' => $address, + 'type' => $type, + ], 'site_dir')[0]; + + if ($type === 'onion') { // Delete Tor config if (unlink(CONF['ht']['tor_config_path'] . '/' . $_SESSION['id'] . '/' . $dir) !== true) output(500, 'Failed to delete Tor configuration.'); @@ -65,13 +85,7 @@ function htDeleteSite($dir, $domainType, $protocol) { } // Delete Nginx config - $domain = query('select', 'sites', [ - 'username' => $_SESSION['id'], - 'domain_type' => $domainType, - 'protocol' => $protocol, - 'site_dir' => $dir, - ], 'domain')[0]; - if (unlink(CONF['ht']['nginx_config_path'] . '/' . $domain . '.conf') !== true) + if (unlink(CONF['ht']['nginx_config_path'] . '/' . $address . '.conf') !== true) output(500, 'Failed to delete Nginx configuration.'); // Reload Nginx @@ -79,9 +93,9 @@ function htDeleteSite($dir, $domainType, $protocol) { if ($code !== 0) output(500, 'Failed to reload Nginx.'); - if ($domainType === 'dns') { + if ($type === 'dns') { // Delete Let's Encrypt certificate - exec(CONF['ht']['sudo_path'] . ' ' . CONF['ht']['certbot_path'] . ' delete --quiet --cert-name ' . $domain, $output, $code); + exec(CONF['ht']['sudo_path'] . ' ' . CONF['ht']['certbot_path'] . ' delete --quiet --cert-name ' . $address, $output, $code); if ($code !== 0) output(500, 'Certbot failed to delete the Let\'s Encrypt certificate.'); } @@ -89,8 +103,7 @@ function htDeleteSite($dir, $domainType, $protocol) { // Delete from database query('delete', 'sites', [ 'username' => $_SESSION['id'], - 'domain_type' => $domainType, - 'protocol' => $protocol, + 'type' => $type, 'site_dir' => $dir, ]); } diff --git a/pages.php b/pages.php index b433d01..220aebe 100644 --- a/pages.php +++ b/pages.php @@ -149,23 +149,29 @@ define('PAGES', [ 'title' => 'Hypertexte', 'description' => 'Mettre en ligne son site statique sur un espace SFTP, et le faire répondre en HTTP par DNS ou Tor', ], - 'add-http-dns' => [ - 'title' => 'Ajouter un accès HTTP par DNS+TLS', - 'description' => 'Ajouter un accès HTTP par ' . linkToDocs('dns', 'DNS') . ' et ' . linkToDocs('tls', 'TLS') . ' sur un sous-dossier de l\'espace SFTP', - 'tokens_account_cost' => 3600, + 'add-subpath' => [ + 'title' => 'Accès par sous-chemin ' . CONF['ht']['subpath_domain'] . '/', + 'description' => 'Son URL ressemblera à https://' . CONF['ht']['subpath_domain'] . '/monsite/', + 'tokens_account_cost' => 900, ], - 'add-http-onion' => [ - 'title' => 'Ajouter un accès HTTP par Onion', - 'description' => 'Ajouter un accès HTTP par ' . linkToDocs('tor', 'service Onion') . ' sur un sous-dossier de l\'espace SFTP', + 'add-subdomain' => [ + 'title' => 'Accès par sous-domaine de .' . CONF['ht']['subdomain_domain'] . '', + 'description' => 'Son URL ressemblera à https://monsite.' . CONF['ht']['subpath_domain'] . '/', 'tokens_account_cost' => 1800, ], - 'del-http-dns' => [ - 'title' => 'Retirer un accès HTTP par DNS+TLS', - 'description' => 'Retirer un accès HTTP par DNS et TLS d\'un sous-dossier de l\'espace SFTP', + 'add-dns' => [ + 'title' => 'Accès par domaine dédié et certificat Let\'s Encrypt', + 'description' => 'Son URL ressemblera à https://monsite.example/', + 'tokens_account_cost' => 3600, ], - 'del-http-onion' => [ - 'title' => 'Retirer un accès HTTP par Onion', - 'description' => 'Retirer un accès HTTP par service Onion d\'un sous-dossier de l\'espace SFTP', + 'add-onion' => [ + 'title' => 'Accès par service Onion', + 'description' => 'Son URL ressemblera à http://nrdselxjgryq5fwek2xh3pxg4b26z26eyzlbs4y5lownk465jhaamayd.onion/, qui ne fonctionnera que par le réseau Tor', + 'tokens_account_cost' => 1800, + ], + 'del' => [ + 'title' => 'Retirer un accès', + 'description' => 'Retirer un accès HTTP déjà existant d\'un sous-dossier de l\'espace SFTP', ], ], ]); diff --git a/pg-act/auth/unregister.php b/pg-act/auth/unregister.php index 23ccdb1..185dbf7 100644 --- a/pg-act/auth/unregister.php +++ b/pg-act/auth/unregister.php @@ -9,19 +9,8 @@ foreach (query('select', 'registry', ['username' => $_SESSION['id']], 'domain') foreach (query('select', 'zones', ['username' => $_SESSION['id']], 'zone') as $zone) nsDeleteZone($zone); -foreach (query('select', 'sites', [ - 'username' => $_SESSION['id'], - 'domain_type' => 'onion', - 'protocol' => 'http', -], 'site_dir') as $dir) - htDeleteSite($dir, domainType: 'onion', protocol: 'http'); - -foreach (query('select', 'sites', [ - 'username' => $_SESSION['id'], - 'domain_type' => 'dns', - 'protocol' => 'http', -], 'site_dir') as $dir) - htDeleteSite($dir, domainType: 'dns', protocol: 'http'); +foreach (query('select', 'sites', ['username' => $_SESSION['id']]) as $site) + htDeleteSite($site['address'], $site['type']); exec(CONF['ht']['sudo_path'] . ' -u ' . CONF['ht']['tor_user'] . ' ' . CONF['ht']['rm_path'] . ' --recursive ' . CONF['ht']['tor_keys_path'] . '/' . $_SESSION['id'], result_code: $code); if ($code !== 0) diff --git a/pg-act/ht/add-http-dns.php b/pg-act/ht/add-dns.php similarity index 93% rename from pg-act/ht/add-http-dns.php rename to pg-act/ht/add-dns.php index 893dc48..8ceebee 100644 --- a/pg-act/ht/add-http-dns.php +++ b/pg-act/ht/add-dns.php @@ -2,7 +2,7 @@ $_POST['domain'] = formatDomain($_POST['domain']); -if (dirsStatuses('dns', 'http')[$_POST['dir']] !== false) +if (dirsStatuses('dns')[$_POST['dir']] !== false) output(403, 'Wrong value for dir.'); if (query('select', 'sites', ['domain' => $_POST['domain']], 'domain') !== []) @@ -30,7 +30,7 @@ checkAuthToken($matches[1], $matches[2]); rateLimit(); -addSite($_SESSION['id'], $_POST['dir'], $_POST['domain'], 'dns', 'http'); +addSite($_SESSION['id'], $_POST['dir'], $_POST['domain'], 'dns'); exec('2>&1 ' . CONF['ht']['sudo_path'] . ' ' . CONF['ht']['certbot_path'] . ' certonly' . (($_SESSION['type'] === 'approved') ? '' : ' --test-cert') . ' --key-type rsa --rsa-key-size 3072 --webroot --webroot-path /srv/niver/acme --domain ' . $_POST['domain'], $output, $returnCode); if ($returnCode !== 0) @@ -56,4 +56,4 @@ exec(CONF['ht']['sudo_path'] . ' ' . CONF['ht']['nginx_reload_cmd'], result_code if ($code !== 0) output(500, 'Failed to reload Nginx.'); -output(200, 'Accès HTTP par domaine ajouté sur ce dossier !'); +output(200, 'Accès HTTP par domaine dédié ajouté sur ce dossier !'); diff --git a/pg-act/ht/add-http-onion.php b/pg-act/ht/add-onion.php similarity index 93% rename from pg-act/ht/add-http-onion.php rename to pg-act/ht/add-onion.php index 679458d..eb078b1 100644 --- a/pg-act/ht/add-http-onion.php +++ b/pg-act/ht/add-onion.php @@ -1,6 +1,6 @@ dir.'); rateLimit(); @@ -24,7 +24,7 @@ if (preg_match('/^[0-9a-z]{56}\.onion$/D', $onion) !== 1) output(500, 'No onion address found.'); // Store it in the database -addSite($_SESSION['id'], $_POST['dir'], $onion, 'onion', 'http'); +addSite($_SESSION['id'], $_POST['dir'], $onion, 'onion'); // Add Nginx config $nginxConf = 'server { diff --git a/pg-act/ht/add-subdomain.php b/pg-act/ht/add-subdomain.php new file mode 100644 index 0000000..2d333b9 --- /dev/null +++ b/pg-act/ht/add-subdomain.php @@ -0,0 +1,19 @@ +dir.'); + +if (preg_match('/^[a-z0-9]{1,32}$/D', $_POST['subdomain']) !== 1) + output(403, 'Label de domaine invalide.'); + +if (query('select', 'sites', ['address' => $_POST['subdomain'], 'type' => 'subdomain']) !== []) + output(403, 'Ce domaine est déjà utilisé sur ce service. Utilisez-en un autre.'); + +rateLimit(); + +addSite($_SESSION['id'], $_POST['dir'], $_POST['subdomain'], 'subdomain'); + +if (symlink(CONF['ht']['ht_path'] . '/' . $_SESSION['id'] . '/' . $_POST['dir'], CONF['ht']['subdomain_path'] . '/' . $_POST['subdomain']) !== true) + output(500, 'Unable to create symlink.'); + +output(200, 'Accès HTTP par sous-chemin ajouté sur ce dossier !'); diff --git a/pg-act/ht/add-subpath.php b/pg-act/ht/add-subpath.php new file mode 100644 index 0000000..3f300b7 --- /dev/null +++ b/pg-act/ht/add-subpath.php @@ -0,0 +1,19 @@ +dir.'); + +if (preg_match('/^[a-z0-9]{1,32}$/D', $_POST['path']) !== 1) + output(403, 'Chemin invalide.'); + +if (query('select', 'sites', ['address' => $_POST['path'], 'type' => 'subpath']) !== []) + output(403, 'Ce chemin est déjà utilisé sur ce service. Utilisez-en un autre.'); + +rateLimit(); + +addSite($_SESSION['id'], $_POST['dir'], $_POST['path'], 'subpath'); + +if (symlink(CONF['ht']['ht_path'] . '/' . $_SESSION['id'] . '/' . $_POST['dir'], CONF['ht']['subpath_path'] . '/' . $_POST['path']) !== true) + output(500, 'Unable to create symlink.'); + +output(200, 'Accès HTTP par sous-chemin ajouté sur ce dossier !'); diff --git a/pg-act/ht/del-http-dns.php b/pg-act/ht/del-http-dns.php deleted file mode 100644 index 9664888..0000000 --- a/pg-act/ht/del-http-dns.php +++ /dev/null @@ -1,8 +0,0 @@ -dir.'); - -htDeleteSite($_POST['dir'], domainType: 'dns', protocol: 'http'); - -output(200, 'Accès retiré.'); diff --git a/pg-act/ht/del-http-onion.php b/pg-act/ht/del-http-onion.php deleted file mode 100644 index fe46a41..0000000 --- a/pg-act/ht/del-http-onion.php +++ /dev/null @@ -1,8 +0,0 @@ -dir.'); - -htDeleteSite($_POST['dir'], domainType: 'onion', protocol: 'http'); - -output(200, 'Accès retiré.'); diff --git a/pg-act/ht/del.php b/pg-act/ht/del.php new file mode 100644 index 0000000..b5fe7b4 --- /dev/null +++ b/pg-act/ht/del.php @@ -0,0 +1,11 @@ +subpath|subdomain|onion|dns):(?
[a-z0-9._-]{1,256})$/D', $_POST['site'], $site) !== 1) + output(403, 'Malformed value for site.'); + +if (isset(query('select', 'sites', ['username' => $_SESSION['id'], 'address' => $site['address'], 'type' => $site['type']], 'address')[0]) !== true) + output(403, 'Unavailable value for site.'); + +htDeleteSite($site['address'], $site['type']); + +output(200, 'Accès retiré.'); diff --git a/pg-view/ht/add-http-dns.php b/pg-view/ht/add-dns.php similarity index 86% rename from pg-view/ht/add-http-dns.php rename to pg-view/ht/add-dns.php index dade10a..067a2a9 100644 --- a/pg-view/ht/add-http-dns.php +++ b/pg-view/ht/add-dns.php @@ -3,7 +3,7 @@

- La présence des enregistrements ci-après sera vérifiée lors du traitement de ce formulaire. + Le domaine doit posséder les enregistrements ci-après lors du traitement de ce formulaire.

@@ -28,7 +28,7 @@ diff --git a/pg-view/ht/add-http-onion.php b/pg-view/ht/add-onion.php similarity index 72% rename from pg-view/ht/add-http-onion.php rename to pg-view/ht/add-onion.php index 3456355..50a6ecd 100644 --- a/pg-view/ht/add-http-onion.php +++ b/pg-view/ht/add-onion.php @@ -1,5 +1,5 @@

- Ajouter un accès en .onion sur un dossier + Ajouter un accès par sur un dossier.

@@ -7,7 +7,7 @@ diff --git a/pg-view/ht/add-subdomain.php b/pg-view/ht/add-subdomain.php new file mode 100644 index 0000000..a8235e1 --- /dev/null +++ b/pg-view/ht/add-subdomain.php @@ -0,0 +1,18 @@ +

+ Ajouter sur un dossier de site un accès par sous-domaine de . +

+ + +
+ .
+
+ +
+ +
diff --git a/pg-view/ht/add-subpath.php b/pg-view/ht/add-subpath.php new file mode 100644 index 0000000..a661a9f --- /dev/null +++ b/pg-view/ht/add-subpath.php @@ -0,0 +1,18 @@ +

+ Ajouter sur un dossier de site un accès par sous-chemin de /. +

+ +
+
+ https:///
+
+ +
+ +
diff --git a/pg-view/ht/del-http-dns.php b/pg-view/ht/del-http-dns.php deleted file mode 100644 index c3b9f68..0000000 --- a/pg-view/ht/del-http-dns.php +++ /dev/null @@ -1,16 +0,0 @@ -

- Retirer un accès DNS et TLS d'un dossier -

- -
-
- -
- -
diff --git a/pg-view/ht/del-http-onion.php b/pg-view/ht/del-http-onion.php deleted file mode 100644 index 958f5c9..0000000 --- a/pg-view/ht/del-http-onion.php +++ /dev/null @@ -1,16 +0,0 @@ -

- Retirer un accès Onion d'un dossier -

- -
-
- -
- -
diff --git a/pg-view/ht/del.php b/pg-view/ht/del.php new file mode 100644 index 0000000..66c44ea --- /dev/null +++ b/pg-view/ht/del.php @@ -0,0 +1,23 @@ +

+ Retirer un accès HTTP d'un dossier +

+ +
+
+ +
+ +
diff --git a/pg-view/ht/index.php b/pg-view/ht/index.php index d2a0391..d029d26 100644 --- a/pg-view/ht/index.php +++ b/pg-view/ht/index.php @@ -15,7 +15,12 @@ if ($sites === []) else { echo '
' . LF; foreach ($sites as $site) { - $url = 'http' . (($site['domain_type'] === 'onion') ? '' : 's') . '://' . $site['domain'] . '/'; + $url = match ($site['type']) { + 'subpath' => 'https://' . CONF['ht']['subpath_domain'] . '/' . $site['address'] . '/', + 'subdomain' => 'https://' . $site['address'] . '.' . CONF['ht']['subdomain_domain'] . '/', + 'onion' => 'http://' . $site['address'] . '/', + 'dns' => 'https://' . $site['address'] . '/', + }; ?>
/
@@ -43,7 +48,7 @@ else { Vous avez accès à un espace SFTP, limité à > 30) >= 1) ? $quotaSize >> 30 . ' ' . linkToDocs('units', 'Gio') : $quotaSize >> 20 . ' ' . linkToDocs('units', 'Mio') -?>. Vous pouvez téléverser vos sites dans /<nom du site>/*. Indiquez les données ci-dessous à votre client SFTP pour y accéder. +?>. Indiquez les données ci-dessous à votre client SFTP pour y accéder.

@@ -107,12 +112,12 @@ echo (($quotaSize >> 30) >= 1) ? $quotaSize >> 30 . ' ' . linkToDocs('units', '<

Compression gzip

- La compression gzip est supportée, si le client le supporte et que le fichier est disponible, chemin.gz est servi au lieu de chemin. + La compression gzip statique est supportée, si le client le supporte et que le fichier est disponible, chemin.gz est servi au lieu de chemin.

Page d'index

- Lors d'une requĂŞte sur un dossier, le premier des fichiers suivants qui existe dans ce dossier est rĂ©pondu : + Lors d'une requĂŞte sur un dossier, le premier des fichiers suivants qui existe dans ce dossier est rĂ©pondu :

  1. index.html
  2. @@ -122,7 +127,7 @@ echo (($quotaSize >> 30) >= 1) ? $quotaSize >> 30 . ' ' . linkToDocs('units', '<

    Page d'erreur 404

    - Lors d'une requĂŞte aboutissant Ă  une erreur 404, le premier des fichiers suivants qui existe Ă  la racine du site est rĂ©pondu : + Lors d'une requĂŞte aboutissant Ă  une erreur 404, le premier des fichiers suivants qui existe Ă  la racine du site est rĂ©pondu :

    1. 404.html
    2. diff --git a/router.php b/router.php index 85f8b93..5d4eee4 100644 --- a/router.php +++ b/router.php @@ -1,5 +1,4 @@