From bd06fc7fbf914679676ad2599124577efe5155b3 Mon Sep 17 00:00:00 2001 From: Miraty Date: Mon, 10 Apr 2023 00:50:42 +0200 Subject: [PATCH] Use Apache - Allows customization through .htaccess - No need to configure or reload a server when adding a site - Content negotiation --- DOCS/configuration.md | 40 ++++++---------- README.md | 5 +- config.ini | 17 ++----- css/main.css | 4 +- fn/ht.php | 63 +++++++++++-------------- http-errors/400.html | 15 ------ http-errors/403.html | 15 ------ http-errors/404.en.html | 19 -------- http-errors/404.fr.html | 19 -------- http-errors/404.php | 5 -- http-errors/405.html | 15 ------ http-errors/410.html | 15 ------ http-errors/418.html | 15 ------ http-errors/500.en.html | 19 -------- http-errors/500.fr.html | 19 -------- http-errors/500.php | 5 -- http-errors/502.html | 19 -------- http-errors/503.en.html | 19 -------- http-errors/503.fr.html | 19 -------- http-errors/503.php | 5 -- http-errors/504.html | 19 -------- http-errors/default-domain.html | 15 ------ http-errors/default-ip.html | 15 ------ http-errors/index.php | 8 ---- http-errors/lib.php | 10 ---- http-errors/unsecure.en.html | 23 ---------- http-errors/unsecure.fr.html | 23 ---------- http-errors/unsecure.php | 7 --- locales/fr/C/LC_MESSAGES/messages.po | 30 +++--------- locales/messages.pot | 24 ++-------- pg-act/auth/unregister.php | 2 +- pg-act/ht/add-dns.php | 30 +++--------- pg-act/ht/add-onion.php | 20 +------- pg-act/ht/add-subdomain.php | 2 +- pg-act/ht/add-subpath.php | 2 +- pg-view/ht/index.php | 69 ++++++++++++++++++++-------- pg-view/ns/caa.php | 2 +- pg-view/ns/cname.php | 2 +- pg-view/ns/dname.php | 2 +- pg-view/ns/ip.php | 2 +- pg-view/ns/loc.php | 2 +- pg-view/ns/mx.php | 2 +- pg-view/ns/ns.php | 2 +- pg-view/ns/srv.php | 2 +- pg-view/ns/sshfp.php | 2 +- pg-view/ns/tlsa.php | 2 +- pg-view/ns/txt.php | 2 +- router.php | 10 ++-- sftpgo-auth.php | 4 +- view.php | 2 +- 50 files changed, 138 insertions(+), 551 deletions(-) delete mode 100644 http-errors/400.html delete mode 100644 http-errors/403.html delete mode 100644 http-errors/404.en.html delete mode 100644 http-errors/404.fr.html delete mode 100644 http-errors/404.php delete mode 100644 http-errors/405.html delete mode 100644 http-errors/410.html delete mode 100644 http-errors/418.html delete mode 100644 http-errors/500.en.html delete mode 100644 http-errors/500.fr.html delete mode 100644 http-errors/500.php delete mode 100644 http-errors/502.html delete mode 100644 http-errors/503.en.html delete mode 100644 http-errors/503.fr.html delete mode 100644 http-errors/503.php delete mode 100644 http-errors/504.html delete mode 100644 http-errors/default-domain.html delete mode 100644 http-errors/default-ip.html delete mode 100644 http-errors/index.php delete mode 100644 http-errors/lib.php delete mode 100644 http-errors/unsecure.en.html delete mode 100644 http-errors/unsecure.fr.html delete mode 100644 http-errors/unsecure.php diff --git a/DOCS/configuration.md b/DOCS/configuration.md index 2c94662..2409c71 100644 --- a/DOCS/configuration.md +++ b/DOCS/configuration.md @@ -101,7 +101,15 @@ Administrator email address published in every SOA record. Ends with a `.`, `@` ### `ht_path` -Filesystem path to the users files base directory. Files of a user are located inside `ht_path//` +Apache can be [chroot](https://httpd.apache.org/docs/current/mod/mod_unixd.html#chrootdir)ed to this directory. + +`/fs//` is the users files base directory. + +`/uri/
/` is automatically reachable by Apache (using [mod_vhost_alias](https://httpd.apache.org/docs/current/mod/mod_vhost_alias.html)) and contains relative symlinks to users managed directories. + +### `user_quota_testing`, `user_quota_approved` + +Maximum bytes a user can use on its SFTP space, depending on its account type. ### `subpath_domain` and `subpath_path` @@ -125,16 +133,6 @@ For the feature of sites in subdomains of a root domain: `https://example./` maps to `/example/` -### `nginx_config_path` - -Filesystem path to the directory that contains configuration files for dedicated sites. - -The `http` block of nginx must contain something like `include /*.conf;` - -### `nginx_reload_cmd` - -Command to execute through sudo to reload the nginx daemon. - ### `tor_config_path` Filesystem path to the directory containing Tor configuration for onion accesses. The full Tor configuration file path is `tor_config_path//` @@ -151,6 +149,10 @@ Linux user as who runs the Tor daemon. Some commands are executed as this user t Command to execute through sudo to reload the Tor daemon. +### `onion_internal_host` + +HTTP Onion services listen on port 80 and forward requests to this host. + ### `sudo_path` Filesystem path to the sudo binary. @@ -198,19 +200,3 @@ Domain name that users need to direct their SFTP clients to. May be the same key ### `public_sftp_port` Network port that users need to direct their SFTP clients to. The common default port is `22`. - -### `https_port` - -Network port where nginx listens. The common default port is `443`. - -### `ipv6_listen_address`, `ipv4_listen_address` - -IP address where nginx listens. May be the same as `ipv6_address` and `ipv4_address`, or `[::]` and `0.0.0.0` to listen on every address available. - -### `http_onion_socket` - -Filesystem path to the unix socket created by nginx and listening for incoming Onion services connections. (Used in Tor and nginx configuration files when creating an Onion service.) - -### `user_quota_testing`, `user_quota_approved` - -Maximum bytes a user can use on its SFTP space, depending on its account type. diff --git a/README.md b/README.md index 9173e6a..283de6d 100644 --- a/README.md +++ b/README.md @@ -54,8 +54,11 @@ Upload site's files to the server using SFTP. The way the site is accessed can t [SFTPGo](https://github.com/drakkan/sftpgo) : upload sites files using SFTP +[Apache HTTP Server](https://httpd.apache.org/) +: static HTTP server, with content negotiation and `.htaccess` dynamic configuration + [nginx](https://nginx.org/) -: static HTTP server +: HTTP reverse proxy for Apache; terminates TLS and enforces security header Tor : [Onion services](https://community.torproject.org/onion-services/) diff --git a/config.ini b/config.ini index c27174d..5c82fd3 100644 --- a/config.ini +++ b/config.ini @@ -30,20 +30,18 @@ public_soa_email = "hostmaster.invalid." [ht] ht_path = "/srv/servnest/ht" +user_quota_testing = 20971520 +user_quota_approved = 209715200 subpath_domain = "ht.servnest.test" -subpath_path = "/srv/servnest/subpath" subdomain_domain = "ht.servnest.test" -subdomain_path = "/srv/servnest/subdomain" - -nginx_config_path = "/srv/servnest/nginx" -nginx_reload_cmd = "/usr/bin/systemctl reload nginx" tor_config_path = "/srv/servnest/tor-config" tor_keys_path = "/srv/servnest/tor-keys" tor_user = "tor" tor_reload_cmd = "/usr/bin/systemctl reload tor" +onion_internal_host = "[::1]:9080" sudo_path = "/usr/bin/sudo" certbot_path = "/usr/bin/certbot" @@ -65,12 +63,3 @@ sftp_fp = "/etc/sftpgo/ed25519.fp" sftp_asciiart = "/etc/sftpgo/ed25519.asciiart" sftp_domain = "sftp.servnest.test" public_sftp_port = 2022 - -; Will be used in configuration files -https_port = 42443 -ipv6_listen_address = "::1" -ipv4_listen_address = "127.0.0.1" -http_onion_socket = "/run/servnest/nginx.sock" - -user_quota_testing = 20971520 -user_quota_approved = 209715200 diff --git a/css/main.css b/css/main.css index c64ad8b..6f586bc 100644 --- a/css/main.css +++ b/css/main.css @@ -89,12 +89,12 @@ a:active { text-decoration-thickness: 0.35em; } -a[rel=help]:before { +a[rel~=help]:before { content: 'ℹ️\202F'; font-size: 0.8rem; } -a[rel=external]:after { +a[rel~=external]:after { content: '\202F↗'; font-size: 0.8rem; } diff --git a/fn/ht.php b/fn/ht.php index 956a6c4..77be8da 100644 --- a/fn/ht.php +++ b/fn/ht.php @@ -3,9 +3,9 @@ function htSetupUserFs($id) { // Setup SFTP directory umask(0002); - if (mkdir(CONF['ht']['ht_path'] . '/' . $id, 0775) !== true) + if (mkdir(CONF['ht']['ht_path'] . '/fs/' . $id, 0775) !== true) output(500, 'Can\'t create user directory.'); - exec(CONF['ht']['sudo_path'] . ' ' . CONF['ht']['chgrp_path'] . ' ' . CONF['ht']['sftpgo_group'] . ' ' . CONF['ht']['ht_path'] . '/' . $id . ' --no-dereference', result_code: $code); + exec(CONF['ht']['sudo_path'] . ' ' . CONF['ht']['chgrp_path'] . ' ' . CONF['ht']['sftpgo_group'] . ' ' . CONF['ht']['ht_path'] . '/fs/' . $id . ' --no-dereference', result_code: $code); if ($code !== 0) output(500, 'Can\'t change user directory group.'); @@ -32,7 +32,7 @@ function formatDomain($domain) { } function listFsDirs($username) { - $absoluteDirs = glob(CONF['ht']['ht_path'] . '/' . $username . '/*/', GLOB_ONLYDIR); + $absoluteDirs = glob(CONF['ht']['ht_path'] . '/fs/' . $username . '/*/', GLOB_ONLYDIR); $dirs = []; foreach ($absoluteDirs as $absoluteDir) if (preg_match('/^[a-zA-Z0-9_-]{1,64}$/D', basename($absoluteDir))) @@ -63,31 +63,23 @@ function dirsStatuses($type) { return $dirs; } +function htRelativeSymlink($target, $name) { + chdir(pathinfo($name)['dirname']); + $symlink = symlink($target, pathinfo($name)['basename']); + chdir(ROOT_PATH); + if ($symlink !== true) + output(500, 'Unable to create symlink.'); +} + function htDeleteSite($address, $type) { - match ($type) { - 'onion', 'dns' => htDeleteDedicatedSite($address, $type), - 'subpath', 'subdomain' => htDeleteSubSite($address, $type) - }; -} - -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') { + $dir = query('select', 'sites', [ + 'username' => $_SESSION['id'], + 'address' => $address, + 'type' => $type, + ], 'site_dir')[0]; + // Delete Tor config if (unlink(CONF['ht']['tor_config_path'] . '/' . $_SESSION['id'] . '/' . $dir) !== true) output(500, 'Failed to delete Tor configuration.'); @@ -103,15 +95,6 @@ function htDeleteDedicatedSite($address, $type) { output(500, 'Failed to delete Tor keys.'); } - // Delete Nginx config - if (unlink(CONF['ht']['nginx_config_path'] . '/' . $address . '.conf') !== true) - output(500, 'Failed to delete Nginx configuration.'); - - // Reload Nginx - exec(CONF['ht']['sudo_path'] . ' ' . CONF['ht']['nginx_reload_cmd'], result_code: $code); - if ($code !== 0) - output(500, 'Failed to reload Nginx.'); - if ($type === 'dns') { // Delete Let's Encrypt certificate exec(CONF['ht']['sudo_path'] . ' ' . CONF['ht']['certbot_path'] . ' delete --quiet --cert-name ' . $address, $output, $code); @@ -119,10 +102,18 @@ function htDeleteDedicatedSite($address, $type) { output(500, 'Certbot failed to delete the Let\'s Encrypt certificate.'); } - // Delete from database + $link = CONF['ht']['ht_path'] . '/uri/' . match ($type) { + 'onion', 'dns' => $address, + 'subdomain' => $address . '.' . CONF['ht']['subdomain_domain'], + 'subpath' => CONF['ht']['subpath_domain'] . '/' . $address, + }; + + if (unlink($link) !== true) + output(500, 'Unable to delete symlink.'); + query('delete', 'sites', [ 'username' => $_SESSION['id'], 'type' => $type, - 'site_dir' => $dir, + 'address' => $address, ]); } diff --git a/http-errors/400.html b/http-errors/400.html deleted file mode 100644 index 6330cbe..0000000 --- a/http-errors/400.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - Bad request · HTTP 400 - - -

Bad request

-

- The request is malformed. -

- HTTP 400 - - diff --git a/http-errors/403.html b/http-errors/403.html deleted file mode 100644 index 1c2fa6e..0000000 --- a/http-errors/403.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - Access forbidden · HTTP 403 - - -

Access forbidden

-

- The server refused to process the request for security reasons. -

- HTTP 403 - - diff --git a/http-errors/404.en.html b/http-errors/404.en.html deleted file mode 100644 index d09c1d5..0000000 --- a/http-errors/404.en.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - File not found · HTTP 404 - - -

File not found

-

- The server did not find anything at the requested address. -

-
    -
  • The file may have been moved or deleted.
  • -
  • Perhaps the address was mistyped.
  • -
- HTTP 404 - - diff --git a/http-errors/404.fr.html b/http-errors/404.fr.html deleted file mode 100644 index b9748e7..0000000 --- a/http-errors/404.fr.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - Fichier introuvable · HTTP 404 - - -

Fichier introuvable

-

- Le serveur n'a rien trouvé à l'adresse demandée. -

-
    -
  • Le fichier a pu être déplacé ou supprimé.
  • -
  • L'adresse a peut-être été mal saisie.
  • -
- HTTP 404 - - diff --git a/http-errors/404.php b/http-errors/404.php deleted file mode 100644 index fe7d286..0000000 --- a/http-errors/404.php +++ /dev/null @@ -1,5 +0,0 @@ - - - - - - Method Not Allowed · HTTP 405 - - -

Method Not Allowed

-

- The request method is not supported for this resource. -

- HTTP 405 - - diff --git a/http-errors/410.html b/http-errors/410.html deleted file mode 100644 index 7b44cd6..0000000 --- a/http-errors/410.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - Gone · HTTP 410 - - -

Gone

-

- The requested resource is not available anymore. -

- HTTP 410 - - diff --git a/http-errors/418.html b/http-errors/418.html deleted file mode 100644 index fcf0bf9..0000000 --- a/http-errors/418.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - I'm a teapot · HTTP 418 - - -

I'm a teapot

-

- Your coffee cannot be brewed because this server is a teapot. -

- HTTP 418 - - diff --git a/http-errors/500.en.html b/http-errors/500.en.html deleted file mode 100644 index 88a41d5..0000000 --- a/http-errors/500.en.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - Server error · HTTP 500 - - -

Server error

-

- The server encountered an error and is unable to satisfy your request. -

-
    -
  • This error is probably temporary.
  • -
  • If it isn't, you can try to contact an administrator.
  • -
- HTTP 500 - - diff --git a/http-errors/500.fr.html b/http-errors/500.fr.html deleted file mode 100644 index 25926e4..0000000 --- a/http-errors/500.fr.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - Erreur du serveur · HTTP 500 - - -

Erreur du serveur

-

- Le serveur a rencontré une erreur et ne peut pas répondre à cette requête. -

-
    -
  • Cette erreur est probablement temporaire.
  • -
  • Si ce n'est pas le cas, vous pouvez contacter ane administrataire.
  • -
- HTTP 500 - - diff --git a/http-errors/500.php b/http-errors/500.php deleted file mode 100644 index 30f23af..0000000 --- a/http-errors/500.php +++ /dev/null @@ -1,5 +0,0 @@ - - - - - - Bad gateway · HTTP 502 - - -

Bad gateway

-

- The backend server encountered an error and is unable to satisfy your request. -

-
    -
  • This error is probably temporary.
  • -
  • If it isn't, you can try to contact an administrator.
  • -
- HTTP 502 - - diff --git a/http-errors/503.en.html b/http-errors/503.en.html deleted file mode 100644 index 7e84963..0000000 --- a/http-errors/503.en.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - Service unavailable · HTTP 503 - - -

Service unavailable

-

- The server cannot handle the request, because it is overloaded or down for maintenance. -

-
    -
  • This error is probably temporary.
  • -
  • If it isn't, you can try to contact an administrator.
  • -
- HTTP 503 - - diff --git a/http-errors/503.fr.html b/http-errors/503.fr.html deleted file mode 100644 index c947e50..0000000 --- a/http-errors/503.fr.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - Service indisponible · HTTP 503 - - -

Service indisponible

-

- Le serveur ne peut pas répondre à cette requête, car il est surchargé ou en cours de maintenance. -

-
    -
  • Cette erreur est probablement temporaire.
  • -
  • Si ce n'est pas le cas, vous pouvez contacter ane administrataire.
  • -
- HTTP 503 - - diff --git a/http-errors/503.php b/http-errors/503.php deleted file mode 100644 index f8ed3c2..0000000 --- a/http-errors/503.php +++ /dev/null @@ -1,5 +0,0 @@ - - - - - - Gateway timeout · HTTP 504 - - -

Gateway timeout

-

- The backend server did not send a timely response. -

-
    -
  • This error is probably temporary.
  • -
  • If it isn't, you can try to contact an administrator.
  • -
- HTTP 504 - - diff --git a/http-errors/default-domain.html b/http-errors/default-domain.html deleted file mode 100644 index c1d3c69..0000000 --- a/http-errors/default-domain.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - Broken site - - -

Broken site

-

- The site you're trying to reach is misconfigured. This domain seems to be pointing to this server, but this server is not aware of a site using this domain. -

- HTTP 404 - - diff --git a/http-errors/default-ip.html b/http-errors/default-ip.html deleted file mode 100644 index b12b3c8..0000000 --- a/http-errors/default-ip.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - Nothing here - - -

Nothing here

-

- You reached the default site of this server directly using its IP address. There's nothing for you here. -

- HTTP 404 - - diff --git a/http-errors/index.php b/http-errors/index.php deleted file mode 100644 index 5181e4f..0000000 --- a/http-errors/index.php +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - TLS required - - -

TLS required

-

- This site does not accept HTTP requests without TLS. -

-

What happened?

-

- You made a request using HTTP without TLS to the server, which refused this for privacy and security reasons, as unsecure HTTP connections can be watched and modified by any device on the way. -

-

How to solve this?

-

- You can replace the URI scheme http by https to tell your client to make the request using TLS. -

- HTTP 403 - - diff --git a/http-errors/unsecure.fr.html b/http-errors/unsecure.fr.html deleted file mode 100644 index 655cb2b..0000000 --- a/http-errors/unsecure.fr.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - Nécessite TLS - - -

Nécessite TLS

-

- Ce site n'accepte pas les connexions HTTP sans TLS. -

-

Que s'est-il passé ?

-

- Vous avez envoyé une requête HTTP sans TLS au serveur, qui l'a refusée pour des raisons de sécurité et de confidentialité. -

-

Comment régler ce problème ?

-

- Vous pouvez remplacer le schéma d'URI http par https pour indiquer à votre client de faire la requête avec TLS. -

- HTTP 403 - - diff --git a/http-errors/unsecure.php b/http-errors/unsecure.php deleted file mode 100644 index 3081f64..0000000 --- a/http-errors/unsecure.php +++ /dev/null @@ -1,7 +0,0 @@ -.htaccess configuration" +msgstr "Configuration par .htaccess" #: pg-view/ht/index.php:116 -msgid "Static gzip compression is supported: if the client supports it and the file is available, path.gz is served instead of path." -msgstr "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." - -#: pg-view/ht/index.php:119 -msgid "Index page" -msgstr "Page d'index" - -#: pg-view/ht/index.php:121 -msgid "When a request hits a directory, the first of the following files that exists inside this directory is served:" -msgstr "Lors d'une requête sur un dossier, le premier des fichiers suivants qui existe dans ce dossier est répondu :" - -#: pg-view/ht/index.php:129 -msgid "404 error page" -msgstr "Page d'erreur 404" - -#: pg-view/ht/index.php:131 -msgid "When a request ends in a 404 error, the first of the following files that exists at the root of the site is served:" -msgstr "Lors d'une requête aboutissant à une erreur 404, le premier des fichiers suivants qui existe à la racine du site est répondu :" +msgid "You can change the way the HTTP server answers to requests in a directory by setting some directives in a file named .htaccess at the root of this directory. Only the following directives are allowed:" +msgstr "Vous pouvez modifier la façon dont le serveur HTTP répond aux requêtes dans un dossier en indiquant des directives dans un fichier nommé .htaccess à la racine de ce dossier. Seules les directives suivantes sont autorisées :" #: pg-view/ns/caa.php:3 msgid "Flag" diff --git a/locales/messages.pot b/locales/messages.pot index c78957b..0f27226 100644 --- a/locales/messages.pot +++ b/locales/messages.pot @@ -407,13 +407,13 @@ msgstr "" msgid "No TXT record with the expected format has been found." msgstr "" -#: pg-act/ht/add-dns.php:59 pg-act/ht/add-onion.php:47 +#: pg-act/ht/add-dns.php:41 pg-act/ht/add-onion.php:31 #: pg-act/ht/add-subdomain.php:19 pg-act/ht/add-subpath.php:19 #, php-format msgid "%s added on this directory." msgstr "" -#: pg-act/ht/add-onion.php:47 +#: pg-act/ht/add-onion.php:31 #, php-format msgid "Its address is: %s" msgstr "" @@ -768,27 +768,11 @@ msgid "A content security policy (CSP) forbids Web browsers from loading JavaScr msgstr "" #: pg-view/ht/index.php:114 -msgid "gzip compression" +msgid ".htaccess configuration" msgstr "" #: pg-view/ht/index.php:116 -msgid "Static gzip compression is supported: if the client supports it and the file is available, path.gz is served instead of path." -msgstr "" - -#: pg-view/ht/index.php:119 -msgid "Index page" -msgstr "" - -#: pg-view/ht/index.php:121 -msgid "When a request hits a directory, the first of the following files that exists inside this directory is served:" -msgstr "" - -#: pg-view/ht/index.php:129 -msgid "404 error page" -msgstr "" - -#: pg-view/ht/index.php:131 -msgid "When a request ends in a 404 error, the first of the following files that exists at the root of the site is served:" +msgid "You can change the way the HTTP server answers to requests in a directory by setting some directives in a file named .htaccess at the root of this directory. Only the following directives are allowed:" msgstr "" #: pg-view/ns/caa.php:3 diff --git a/pg-act/auth/unregister.php b/pg-act/auth/unregister.php index 708da1c..c80dffa 100644 --- a/pg-act/auth/unregister.php +++ b/pg-act/auth/unregister.php @@ -30,7 +30,7 @@ if (in_array('ht', $user_services, true)) { removeDirectory(CONF['ht']['tor_config_path'] . '/' . $_SESSION['id']); - exec(CONF['ht']['sudo_path'] . ' -u ' . CONF['ht']['sftpgo_user'] . ' ' . CONF['ht']['rm_path'] . ' --recursive ' . CONF['ht']['ht_path'] . '/' . $_SESSION['id'], result_code: $code); + exec(CONF['ht']['sudo_path'] . ' -u ' . CONF['ht']['sftpgo_user'] . ' ' . CONF['ht']['rm_path'] . ' --recursive ' . CONF['ht']['ht_path'] . '/fs/' . $_SESSION['id'], result_code: $code); if ($code !== 0) output(500, 'Can\'t remove user\'s directory.'); } diff --git a/pg-act/ht/add-dns.php b/pg-act/ht/add-dns.php index 118303e..d82e4a8 100644 --- a/pg-act/ht/add-dns.php +++ b/pg-act/ht/add-dns.php @@ -6,23 +6,23 @@ if (dirsStatuses('dns')[$_POST['dir']] !== false) output(403, 'Wrong value for dir.'); if (query('select', 'sites', ['domain' => $_POST['domain']], 'domain') !== []) - output(403, _('This domain already exists on this service. Use another one.'); + output(403, _('This domain already exists on this service. Use another one.')); $remoteAaaaRecords = dns_get_record($_POST['domain'], DNS_AAAA); if (is_array($remoteAaaaRecords) !== true) - output(500, sprintf(_('Can\'t retrieve the %s record.'), 'AAAA'); + output(500, sprintf(_('Can\'t retrieve the %s record.'), 'AAAA')); if (equalArrays([CONF['ht']['ipv6_address']], array_column($remoteAaaaRecords, 'ipv6')) !== true) output(403, sprintf(_('This domain must have %2$s as its only %1$s record.'), 'AAAA', '' . CONF['ht']['ipv6_address'] . '')); $remoteARecords = dns_get_record($_POST['domain'], DNS_A); if (is_array($remoteARecords) !== true) - output(500, sprintf(_('Can\'t retrieve the %s record.'), 'A'); + output(500, sprintf(_('Can\'t retrieve the %s record.'), 'A')); if (equalArrays([CONF['ht']['ipv4_address']], array_column($remoteARecords, 'ip')) !== true) output(403, sprintf(_('This domain must have %2$s as its only %1$s record.'), 'A', '' . CONF['ht']['ipv4_address'] . '')); $remoteTXTRecords = dns_get_record($_POST['domain'], DNS_TXT); if (is_array($remoteTXTRecords) !== true) - output(500, sprintf(_('Can\'t retrieve the %s record.'), 'TXT'); + output(500, sprintf(_('Can\'t retrieve the %s record.'), 'TXT')); if (preg_match('/^' . preg_quote(SERVER_NAME, '/') . '_domain-verification=([0-9a-f]{8})-([0-9a-f]{32})$/Dm', implode(LF, array_column($remoteTXTRecords, 'txt')), $matches) !== 1) output(403, _('No TXT record with the expected format has been found.')); @@ -36,24 +36,6 @@ exec('2>&1 ' . CONF['ht']['sudo_path'] . ' ' . CONF['ht']['certbot_path'] . ' ce if ($returnCode !== 0) output(500, 'Certbot failed to get a Let\'s Encrypt certificate.', $output); -$nginxConf = 'server { - listen [' . CONF['ht']['ipv6_listen_address'] . ']:' . CONF['ht']['https_port'] . ' ssl http2; - listen ' . CONF['ht']['ipv4_listen_address'] . ':' . CONF['ht']['https_port'] . ' ssl http2; - server_name ' . $_POST['domain'] . '; - root ' . CONF['ht']['ht_path'] . '/' . $_SESSION['id'] . '/' . $_POST['dir'] . '; +htRelativeSymlink('../fs/' . $_SESSION['id'] . '/' . $_POST['dir'], CONF['ht']['ht_path'] . '/uri/' . $_POST['domain']); - ssl_certificate /etc/letsencrypt/live/' . $_POST['domain'] . '/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/' . $_POST['domain'] . '/privkey.pem; - - include inc/ht-tls.conf; -} -'; -if (file_put_contents(CONF['ht']['nginx_config_path'] . '/' . $_POST['domain'] . '.conf', $nginxConf) === false) - output(500, 'Failed to write Nginx configuration.'); - -// Reload Nginx -exec(CONF['ht']['sudo_path'] . ' ' . CONF['ht']['nginx_reload_cmd'], result_code: $code); -if ($code !== 0) - output(500, 'Failed to reload Nginx.'); - -output(200, sprintf(_('%s added on this directory.'), PAGE_METADATA['title']); +output(200, sprintf(_('%s added on this directory.'), PAGE_METADATA['title'])); diff --git a/pg-act/ht/add-onion.php b/pg-act/ht/add-onion.php index bf39f61..93b674a 100644 --- a/pg-act/ht/add-onion.php +++ b/pg-act/ht/add-onion.php @@ -7,8 +7,7 @@ rateLimit(); // Add Tor config $torConf = 'HiddenServiceDir ' . CONF['ht']['tor_keys_path'] . '/' . $_SESSION['id'] . '/' . $_POST['dir'] . '/ -HiddenServicePort 80 unix:' . CONF['ht']['http_onion_socket'] . ' -'; +HiddenServicePort 80 ' . CONF['ht']['onion_internal_host'] . LF; if (file_put_contents(CONF['ht']['tor_config_path'] . '/' . $_SESSION['id'] . '/' . $_POST['dir'], $torConf) === false) output(500, 'Failed to write new Tor configuration.'); @@ -26,22 +25,7 @@ if (preg_match('/^[0-9a-z]{56}\.onion$/D', $onion) !== 1) // Store it in the database addSite($_SESSION['id'], $_POST['dir'], $onion, 'onion'); -// Add Nginx config -$nginxConf = 'server { - listen unix:' . CONF['ht']['http_onion_socket'] . '; - server_name ' . $onion . '; - root ' . CONF['ht']['ht_path'] . '/' . $_SESSION['id'] . '/' . $_POST['dir'] . '; - - include inc/ht-onion.conf; -} -'; -if (file_put_contents(CONF['ht']['nginx_config_path'] . '/' . $onion . '.conf', $nginxConf) === false) - output(500, 'Failed to write Nginx configuration.'); - -// Reload Nginx -exec(CONF['ht']['sudo_path'] . ' ' . CONF['ht']['nginx_reload_cmd'], result_code: $code); -if ($code !== 0) - output(500, 'Failed to reload Nginx.'); +htRelativeSymlink('../fs/' . $_SESSION['id'] . '/' . $_POST['dir'], CONF['ht']['ht_path'] . '/uri/' . $onion); // Tell the user their site address output(200, sprintf(_('%s added on this directory.'), PAGE_METADATA['title']) . ' ' . sprintf(_('Its address is: %s'), 'http://' . $onion . '/')); diff --git a/pg-act/ht/add-subdomain.php b/pg-act/ht/add-subdomain.php index 30d35ef..8bd2c96 100644 --- a/pg-act/ht/add-subdomain.php +++ b/pg-act/ht/add-subdomain.php @@ -13,7 +13,7 @@ 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) +if (symlink('../../fs/' . $_SESSION['id'] . '/' . $_POST['dir'], CONF['ht']['ht_path'] . '/uri/' . $_POST['subdomain'] . '.' . CONF['ht']['subdomain_domain']) !== true) output(500, 'Unable to create symlink.'); output(200, sprintf(_('%s added on this directory.'), PAGE_METADATA['title'])); diff --git a/pg-act/ht/add-subpath.php b/pg-act/ht/add-subpath.php index e4b129f..8f83761 100644 --- a/pg-act/ht/add-subpath.php +++ b/pg-act/ht/add-subpath.php @@ -13,7 +13,7 @@ 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) +if (symlink('../../fs/' . $_SESSION['id'] . '/' . $_POST['dir'], CONF['ht']['ht_path'] . '/uri/' . CONF['ht']['subpath_domain'] . '/' . $_POST['path']) !== true) output(500, 'Unable to create symlink.'); output(200, sprintf(_('%s added on this directory.'), PAGE_METADATA['title'])); diff --git a/pg-view/ht/index.php b/pg-view/ht/index.php index c196d38..28c8168 100644 --- a/pg-view/ht/index.php +++ b/pg-view/ht/index.php @@ -111,28 +111,57 @@ $quota = ($_SESSION['type'] ?? '' === 'approved') ? CONF['ht']['user_quota_appro

-

+

.htaccess configuration') ?>

- gzip compression is supported: if the client supports it and the file is available, path.gz is served instead of path.') ?> + .htaccess at the root of this directory. Only the following directives are allowed:') ?>

+ diff --git a/pg-view/ns/caa.php b/pg-view/ns/caa.php index b536a0d..fe3a3f9 100644 --- a/pg-view/ns/caa.php +++ b/pg-view/ns/caa.php @@ -1,5 +1,5 @@
- +
diff --git a/pg-view/ns/cname.php b/pg-view/ns/cname.php index cf9bc28..8c97807 100644 --- a/pg-view/ns/cname.php +++ b/pg-view/ns/cname.php @@ -1,5 +1,5 @@ - +
diff --git a/pg-view/ns/dname.php b/pg-view/ns/dname.php index 5a5df27..40a748a 100644 --- a/pg-view/ns/dname.php +++ b/pg-view/ns/dname.php @@ -1,5 +1,5 @@ - +
diff --git a/pg-view/ns/ip.php b/pg-view/ns/ip.php index 8c6cb49..51d8392 100644 --- a/pg-view/ns/ip.php +++ b/pg-view/ns/ip.php @@ -1,5 +1,5 @@ - +

diff --git a/pg-view/ns/loc.php b/pg-view/ns/loc.php index 70b66c5..5344a94 100644 --- a/pg-view/ns/loc.php +++ b/pg-view/ns/loc.php @@ -1,5 +1,5 @@ - +
diff --git a/pg-view/ns/mx.php b/pg-view/ns/mx.php index 601b378..dc8c933 100644 --- a/pg-view/ns/mx.php +++ b/pg-view/ns/mx.php @@ -1,5 +1,5 @@ - +
diff --git a/pg-view/ns/ns.php b/pg-view/ns/ns.php index 419b894..0cd6fef 100644 --- a/pg-view/ns/ns.php +++ b/pg-view/ns/ns.php @@ -1,5 +1,5 @@ - +
diff --git a/pg-view/ns/srv.php b/pg-view/ns/srv.php index 30893d6..4972852 100644 --- a/pg-view/ns/srv.php +++ b/pg-view/ns/srv.php @@ -1,5 +1,5 @@ - +
diff --git a/pg-view/ns/sshfp.php b/pg-view/ns/sshfp.php index 9e1e32e..4f72ea7 100644 --- a/pg-view/ns/sshfp.php +++ b/pg-view/ns/sshfp.php @@ -1,5 +1,5 @@ - +
diff --git a/pg-view/ns/tlsa.php b/pg-view/ns/tlsa.php index e99df32..3e0a812 100644 --- a/pg-view/ns/tlsa.php +++ b/pg-view/ns/tlsa.php @@ -1,5 +1,5 @@ - +
diff --git a/pg-view/ns/txt.php b/pg-view/ns/txt.php index ea642eb..5cd0f7b 100644 --- a/pg-view/ns/txt.php +++ b/pg-view/ns/txt.php @@ -1,5 +1,5 @@ - +
diff --git a/router.php b/router.php index 33e18a0..38299a3 100644 --- a/router.php +++ b/router.php @@ -10,7 +10,7 @@ date_default_timezone_set('UTC'); $locale = 'en'; foreach (explode(',', preg_replace('/[A-Z0-9]|q=|;|-|\./', '', $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '')) as $client_locale) { - if (in_array($client_locale, array_diff(scandir('locales'), ['..', '.']), true)) { + if (in_array($client_locale, array_diff(scandir(ROOT_PATH . '/locales'), ['..', '.']), true)) { $locale = $client_locale; break; } @@ -31,7 +31,7 @@ const PLACEHOLDER_IPV4 = '203.0.113.42'; // From RFC5737: IPv4 Address Blocks Re foreach (array_diff(scandir(ROOT_PATH . '/fn'), ['..', '.']) as $file) require ROOT_PATH . '/fn/' . $file; -require 'pages.php'; +require ROOT_PATH . '/pages.php'; if ($_SERVER['REQUEST_URI'] === '/sftpgo-auth.php') return; @@ -150,12 +150,12 @@ if ($_POST !== []) { output(403, _('This account doesn\'t exist anymore. Log out to end this ghost session.')); } - if (file_exists('pg-act/' . PAGE_ADDRESS . '.php')) - require 'pg-act/' . PAGE_ADDRESS . '.php'; + if (file_exists(ROOT_PATH . '/pg-act/' . PAGE_ADDRESS . '.php')) + require ROOT_PATH . '/pg-act/' . PAGE_ADDRESS . '.php'; } function displayPage($data) { - require 'view.php'; + require ROOT_PATH . '/view.php'; exit(); } displayPage($data ??= NULL); diff --git a/sftpgo-auth.php b/sftpgo-auth.php index 16741c8..cdd9beb 100644 --- a/sftpgo-auth.php +++ b/sftpgo-auth.php @@ -10,7 +10,7 @@ function deny() { if (CONF['common']['services']['ht'] !== 'enabled') deny(); -$auth_data = json_decode(file_get_contents('php://input'), true); +$auth_data = json_decode(file_get_contents('php://input'), true, flags: JSON_THROW_ON_ERROR); $username = hashUsername($auth_data['username']); @@ -29,7 +29,7 @@ echo ' { "status": 1, "username": ' . json_encode($auth_data['username']) . ', - "home_dir": "' . CONF['ht']['ht_path'] . '/' . $id . '", + "home_dir": "' . CONF['ht']['ht_path'] . '/fs/' . $id . '", "quota_size": ' . ((query('select', 'users', ['id' => $id], 'type')[0] === 'approved') ? CONF['ht']['user_quota_approved'] : CONF['ht']['user_quota_testing']) . ', "permissions": { "/": [ diff --git a/view.php b/view.php index 7f98c73..1f30b8d 100644 --- a/view.php +++ b/view.php @@ -38,7 +38,7 @@ if (in_array(SERVICE, SERVICES_USER, true) AND CONF['common']['services'][SERVICE] === 'error') echo '

' . _('This service is currently under maintenance. No action can be taken on it until an administrator finishes repairing it.') . '

'; - require 'pg-view/' . PAGE_ADDRESS . '.php'; + require ROOT_PATH . '/pg-view/' . PAGE_ADDRESS . '.php'; if ($_POST === [] AND PAGE_METADATA['require-login'] ?? true !== false AND !isset($_SESSION['id']) AND PAGE_TERMINAL) echo '

' . sprintf(_('This form won\'t be accepted because you need to %slog in%s first.'), '', '') . '

';