Allow SSH keys authentication for SFTP(Go)
This commit is contained in:
parent
256bd51e0f
commit
067e1ccf42
13 changed files with 255 additions and 57 deletions
|
@ -24,7 +24,7 @@ I plan to create and maintain a public stable instance of ServNest, but I haven'
|
||||||
### Name server (`ns`)
|
### Name server (`ns`)
|
||||||
|
|
||||||
* Host a zone on the server
|
* Host a zone on the server
|
||||||
* Zone file edition through `<textarea>`
|
* Plain zone file edition
|
||||||
* Dedicated forms to set/unset `A`, `AAAA`, `NS`, `TXT`, `CAA`, `SRV`, `MX`, `SRV`, `SSHFP`, `TLSA`, `CNAME`, `DNAME` and `LOC` records
|
* Dedicated forms to set/unset `A`, `AAAA`, `NS`, `TXT`, `CAA`, `SRV`, `MX`, `SRV`, `SSHFP`, `TLSA`, `CNAME`, `DNAME` and `LOC` records
|
||||||
* Display records or the full zone file
|
* Display records or the full zone file
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,14 @@ input[type=password] {
|
||||||
width: 7ch;
|
width: 7ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#public-key {
|
||||||
|
width: 70ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
#key {
|
||||||
|
width: 65ch;
|
||||||
|
}
|
||||||
|
|
||||||
:disabled {
|
:disabled {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ h3 {
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
main > *:not(pre, form), form > *:not(textarea), footer {
|
main > *:not(pre, form), footer {
|
||||||
max-width: 40rem;
|
max-width: 40rem;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
|
|
11
db/migrations/005-create-ssh-keys-table.sql
Normal file
11
db/migrations/005-create-ssh-keys-table.sql
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
BEGIN TRANSACTION;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "ssh-keys" (
|
||||||
|
"key" TEXT NOT NULL,
|
||||||
|
"username" TEXT NOT NULL,
|
||||||
|
"directory" TEXT NOT NULL,
|
||||||
|
UNIQUE("key", "username", "directory"),
|
||||||
|
FOREIGN KEY("username") REFERENCES "users"("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMIT;
|
|
@ -53,4 +53,11 @@ CREATE TABLE IF NOT EXISTS "sites" (
|
||||||
PRIMARY KEY("address", "type"),
|
PRIMARY KEY("address", "type"),
|
||||||
FOREIGN KEY("username") REFERENCES "users"("id")
|
FOREIGN KEY("username") REFERENCES "users"("id")
|
||||||
);
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS "ssh-keys" (
|
||||||
|
"key" TEXT NOT NULL,
|
||||||
|
"username" TEXT NOT NULL,
|
||||||
|
"directory" TEXT NOT NULL,
|
||||||
|
UNIQUE("key", "username", "directory"),
|
||||||
|
FOREIGN KEY("username") REFERENCES "users"("id")
|
||||||
|
);
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
const SUBPATH_REGEX = '^[a-z0-9-]{4,63}$';
|
const SUBPATH_REGEX = '^[a-z0-9-]{4,63}$';
|
||||||
|
const ED25519_PUBKEY_REGEX = '^[a-zA-Z0-9/+]{68}$';
|
||||||
|
|
||||||
function htSetupUserFs($id) {
|
function htSetupUserFs($id) {
|
||||||
// Setup SFTP directory
|
// Setup SFTP directory
|
||||||
|
@ -37,6 +38,8 @@ function formatDomain($domain) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function listFsDirs($username) {
|
function listFsDirs($username) {
|
||||||
|
if ($username === '')
|
||||||
|
return [];
|
||||||
$absoluteDirs = glob(CONF['ht']['ht_path'] . '/fs/' . $username . '/*/', GLOB_ONLYDIR);
|
$absoluteDirs = glob(CONF['ht']['ht_path'] . '/fs/' . $username . '/*/', GLOB_ONLYDIR);
|
||||||
$dirs = [];
|
$dirs = [];
|
||||||
foreach ($absoluteDirs as $absoluteDir)
|
foreach ($absoluteDirs as $absoluteDir)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-06-04 23:57+0200\n"
|
"POT-Creation-Date: 2023-06-15 01:33+0200\n"
|
||||||
"Language: fr\n"
|
"Language: fr\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
|
||||||
|
@ -274,18 +274,26 @@ msgstr "Supprimer un accès"
|
||||||
msgid "Delete an existing HTTP access from a subdirectory of the SFTP space"
|
msgid "Delete an existing HTTP access from a subdirectory of the SFTP space"
|
||||||
msgstr "Retirer un accès HTTP existant d'un sous-dossier de l'espace SFTP"
|
msgstr "Retirer un accès HTTP existant d'un sous-dossier de l'espace SFTP"
|
||||||
|
|
||||||
#: router.php:152 view.php:39
|
#: pages.php:197
|
||||||
|
msgid "Manage SSH keys"
|
||||||
|
msgstr "Gérer les clés SSH"
|
||||||
|
|
||||||
|
#: pages.php:198
|
||||||
|
msgid "Choose what SSH key can edit what directory"
|
||||||
|
msgstr "Choisir quelle clé SSH peut modifier quel dossier"
|
||||||
|
|
||||||
|
#: router.php:68
|
||||||
|
msgid "This account doesn't exist anymore. Log out to end this ghost session."
|
||||||
|
msgstr "Ce compte n'existe plus. Déconnectez-vous pour terminer cette session fantôme."
|
||||||
|
|
||||||
|
#: router.php:106 view.php:39
|
||||||
msgid "This service is currently under maintenance. No action can be taken on it until an administrator finishes repairing it."
|
msgid "This service is currently under maintenance. No action can be taken on it until an administrator finishes repairing it."
|
||||||
msgstr "Ce service est en cours de maintenance. Aucune action ne peut être effectuée avant qu'ane administrataire termine de le réparer."
|
msgstr "Ce service est en cours de maintenance. Aucune action ne peut être effectuée avant qu'ane administrataire termine de le réparer."
|
||||||
|
|
||||||
#: router.php:162
|
#: router.php:115
|
||||||
msgid "You need to be logged in to do this."
|
msgid "You need to be logged in to do this."
|
||||||
msgstr "Vous devez être connecté·e à un compte pour faire cela."
|
msgstr "Vous devez être connecté·e à un compte pour faire cela."
|
||||||
|
|
||||||
#: router.php:164
|
|
||||||
msgid "This account doesn't exist anymore. Log out to end this ghost session."
|
|
||||||
msgstr "Ce compte n'existe plus. Déconnectez-vous pour terminer cette session fantôme."
|
|
||||||
|
|
||||||
#: view.php:19
|
#: view.php:19
|
||||||
msgid "You are using a testing account. It may be deleted anytime."
|
msgid "You are using a testing account. It may be deleted anytime."
|
||||||
msgstr "Vous utilisez un compte de test. Il risque d'être supprimé n'importe quand."
|
msgstr "Vous utilisez un compte de test. Il risque d'être supprimé n'importe quand."
|
||||||
|
@ -308,11 +316,16 @@ msgstr "Ce formulaire ne sera pas accepté car il faut %sse connecter%s d'abord.
|
||||||
msgid "%sSource code%s available under %s."
|
msgid "%sSource code%s available under %s."
|
||||||
msgstr "%sCode source%s disponible sous %s."
|
msgstr "%sCode source%s disponible sous %s."
|
||||||
|
|
||||||
#: fn/auth.php:110
|
#: fn/auth.php:95
|
||||||
|
#, php-format
|
||||||
|
msgid "Your account can't be deleted because the %s service is currently unavailable."
|
||||||
|
msgstr "Votre compte ne peut pas être supprimé car le service %s est actuellement indisponible."
|
||||||
|
|
||||||
|
#: fn/auth.php:143
|
||||||
msgid "Account rate limit reached, try again later."
|
msgid "Account rate limit reached, try again later."
|
||||||
msgstr "Limite de taux pour ce compte atteinte, réessayez plus tard."
|
msgstr "Limite de taux pour ce compte atteinte, réessayez plus tard."
|
||||||
|
|
||||||
#: fn/auth.php:135
|
#: fn/auth.php:168
|
||||||
msgid "Global rate limit reached, try again later."
|
msgid "Global rate limit reached, try again later."
|
||||||
msgstr "Limite de taux globale atteinte, réessayez plus tard."
|
msgstr "Limite de taux globale atteinte, réessayez plus tard."
|
||||||
|
|
||||||
|
@ -328,15 +341,15 @@ msgstr "<strong>Erreur de l'utilisataire</strong> : "
|
||||||
msgid "<strong>Server error</strong>: "
|
msgid "<strong>Server error</strong>: "
|
||||||
msgstr "<strong>Erreur du serveur</strong> : "
|
msgstr "<strong>Erreur du serveur</strong> : "
|
||||||
|
|
||||||
#: fn/common.php:129
|
#: fn/common.php:132
|
||||||
msgid "Wrong proof."
|
msgid "Wrong proof."
|
||||||
msgstr "Preuve incorrecte."
|
msgstr "Preuve incorrecte."
|
||||||
|
|
||||||
#: fn/dns.php:63
|
#: fn/dns.php:64
|
||||||
msgid "IP address malformed."
|
msgid "IP address malformed."
|
||||||
msgstr "Adresse IP malformée."
|
msgstr "Adresse IP malformée."
|
||||||
|
|
||||||
#: fn/dns.php:68 fn/ht.php:30
|
#: fn/dns.php:69 fn/ht.php:31
|
||||||
msgid "Domain malformed."
|
msgid "Domain malformed."
|
||||||
msgstr "Domaine malformé."
|
msgstr "Domaine malformé."
|
||||||
|
|
||||||
|
@ -396,11 +409,6 @@ msgid "Account deletion must be confirmed."
|
||||||
msgstr "La suppression du compte doit être confirmée."
|
msgstr "La suppression du compte doit être confirmée."
|
||||||
|
|
||||||
#: pg-act/auth/unregister.php:13
|
#: pg-act/auth/unregister.php:13
|
||||||
#, php-format
|
|
||||||
msgid "Your account can't be deleted because the %s service is currently unavailable."
|
|
||||||
msgstr "Votre compte ne peut pas être supprimé car le service %s est actuellement indisponible."
|
|
||||||
|
|
||||||
#: pg-act/auth/unregister.php:42
|
|
||||||
msgid "Account deleted."
|
msgid "Account deleted."
|
||||||
msgstr "Compte supprimé."
|
msgstr "Compte supprimé."
|
||||||
|
|
||||||
|
@ -453,6 +461,18 @@ msgstr "Ce chemin est déjà pris sur ce service. Utilisez-en un autre."
|
||||||
msgid "Access removed."
|
msgid "Access removed."
|
||||||
msgstr "Accès retiré."
|
msgstr "Accès retiré."
|
||||||
|
|
||||||
|
#: pg-act/ht/keys.php:13
|
||||||
|
msgid "Path is not valid."
|
||||||
|
msgstr "Le chemin n'est pas valide."
|
||||||
|
|
||||||
|
#: pg-act/ht/keys.php:15
|
||||||
|
msgid "Ed25519 public key seems wrongly formatted."
|
||||||
|
msgstr "La clé public Ed25519 semble mal formattée."
|
||||||
|
|
||||||
|
#: pg-act/ht/keys.php:39
|
||||||
|
msgid "SSH keys updated."
|
||||||
|
msgstr "Clés SSH mises à jour."
|
||||||
|
|
||||||
#: pg-act/ns/caa.php:25 pg-act/ns/cname.php:16 pg-act/ns/dname.php:16
|
#: pg-act/ns/caa.php:25 pg-act/ns/cname.php:16 pg-act/ns/dname.php:16
|
||||||
#: pg-act/ns/ip.php:16 pg-act/ns/loc.php:72 pg-act/ns/mx.php:20
|
#: pg-act/ns/ip.php:16 pg-act/ns/loc.php:72 pg-act/ns/mx.php:20
|
||||||
#: pg-act/ns/ns.php:16 pg-act/ns/srv.php:28 pg-act/ns/sshfp.php:25
|
#: pg-act/ns/ns.php:16 pg-act/ns/srv.php:28 pg-act/ns/sshfp.php:25
|
||||||
|
@ -813,6 +833,30 @@ msgstr "Approuvé"
|
||||||
msgid "Stable Let's Encrypt certificates"
|
msgid "Stable Let's Encrypt certificates"
|
||||||
msgstr "Vrai certificat Let's Encrypt"
|
msgstr "Vrai certificat Let's Encrypt"
|
||||||
|
|
||||||
|
#: pg-view/ht/keys.php:2
|
||||||
|
msgid "In addition to your password, you can also access your SFTP space using Ed25519 SSH keys. A key can be granted modification rights to the full space (<code>/</code>) or to any arbitrary subdirectory. A key is always allowed to list any directory content."
|
||||||
|
msgstr "En plus de la clé de passe, c'est également possible d'accéder à l'espace SFTP en utilisant des clés SSH Ed25519. Une clé peut être autorisée à modifier dans tout l'espace (<code>/</code>) ou dans un quelconque sous-dossier spécifique. Une clé est toujours autorisée à lister le contenu de n'importe quel dossier."
|
||||||
|
|
||||||
|
#: pg-view/ht/keys.php:17
|
||||||
|
msgid "Add new SSH key access"
|
||||||
|
msgstr "Ajouter un nouvel accès par clé SSH"
|
||||||
|
|
||||||
|
#: pg-view/ht/keys.php:17
|
||||||
|
msgid "SSH key access"
|
||||||
|
msgstr "Accès par clé SSH"
|
||||||
|
|
||||||
|
#: pg-view/ht/keys.php:19
|
||||||
|
msgid "Public key"
|
||||||
|
msgstr "Clé publique"
|
||||||
|
|
||||||
|
#: pg-view/ht/keys.php:23
|
||||||
|
msgid "Allowed directory"
|
||||||
|
msgstr "Dossier autorisé"
|
||||||
|
|
||||||
|
#: pg-view/ht/keys.php:30
|
||||||
|
msgid "Update"
|
||||||
|
msgstr "Mettre à jour"
|
||||||
|
|
||||||
#: pg-view/ns/caa.php:3
|
#: pg-view/ns/caa.php:3
|
||||||
msgid "Flag"
|
msgid "Flag"
|
||||||
msgstr "Flag"
|
msgstr "Flag"
|
||||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-06-04 23:57+0200\n"
|
"POT-Creation-Date: 2023-06-15 01:33+0200\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -286,18 +286,26 @@ msgstr ""
|
||||||
msgid "Delete an existing HTTP access from a subdirectory of the SFTP space"
|
msgid "Delete an existing HTTP access from a subdirectory of the SFTP space"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: router.php:152 view.php:39
|
#: pages.php:197
|
||||||
|
msgid "Manage SSH keys"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pages.php:198
|
||||||
|
msgid "Choose what SSH key can edit what directory"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: router.php:68
|
||||||
|
msgid "This account doesn't exist anymore. Log out to end this ghost session."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: router.php:106 view.php:39
|
||||||
msgid "This service is currently under maintenance. No action can be taken on it until an administrator finishes repairing it."
|
msgid "This service is currently under maintenance. No action can be taken on it until an administrator finishes repairing it."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: router.php:162
|
#: router.php:115
|
||||||
msgid "You need to be logged in to do this."
|
msgid "You need to be logged in to do this."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: router.php:164
|
|
||||||
msgid "This account doesn't exist anymore. Log out to end this ghost session."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: view.php:19
|
#: view.php:19
|
||||||
msgid "You are using a testing account. It may be deleted anytime."
|
msgid "You are using a testing account. It may be deleted anytime."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
@ -320,11 +328,16 @@ msgstr ""
|
||||||
msgid "%sSource code%s available under %s."
|
msgid "%sSource code%s available under %s."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: fn/auth.php:110
|
#: fn/auth.php:95
|
||||||
|
#, php-format
|
||||||
|
msgid "Your account can't be deleted because the %s service is currently unavailable."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: fn/auth.php:143
|
||||||
msgid "Account rate limit reached, try again later."
|
msgid "Account rate limit reached, try again later."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: fn/auth.php:135
|
#: fn/auth.php:168
|
||||||
msgid "Global rate limit reached, try again later."
|
msgid "Global rate limit reached, try again later."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -340,15 +353,15 @@ msgstr ""
|
||||||
msgid "<strong>Server error</strong>: "
|
msgid "<strong>Server error</strong>: "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: fn/common.php:129
|
#: fn/common.php:132
|
||||||
msgid "Wrong proof."
|
msgid "Wrong proof."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: fn/dns.php:63
|
#: fn/dns.php:64
|
||||||
msgid "IP address malformed."
|
msgid "IP address malformed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: fn/dns.php:68 fn/ht.php:30
|
#: fn/dns.php:69 fn/ht.php:31
|
||||||
msgid "Domain malformed."
|
msgid "Domain malformed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -408,11 +421,6 @@ msgid "Account deletion must be confirmed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: pg-act/auth/unregister.php:13
|
#: pg-act/auth/unregister.php:13
|
||||||
#, php-format
|
|
||||||
msgid "Your account can't be deleted because the %s service is currently unavailable."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: pg-act/auth/unregister.php:42
|
|
||||||
msgid "Account deleted."
|
msgid "Account deleted."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -465,6 +473,18 @@ msgstr ""
|
||||||
msgid "Access removed."
|
msgid "Access removed."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: pg-act/ht/keys.php:13
|
||||||
|
msgid "Path is not valid."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pg-act/ht/keys.php:15
|
||||||
|
msgid "Ed25519 public key seems wrongly formatted."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pg-act/ht/keys.php:39
|
||||||
|
msgid "SSH keys updated."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: pg-act/ns/caa.php:25 pg-act/ns/cname.php:16 pg-act/ns/dname.php:16
|
#: pg-act/ns/caa.php:25 pg-act/ns/cname.php:16 pg-act/ns/dname.php:16
|
||||||
#: pg-act/ns/ip.php:16 pg-act/ns/loc.php:72 pg-act/ns/mx.php:20
|
#: pg-act/ns/ip.php:16 pg-act/ns/loc.php:72 pg-act/ns/mx.php:20
|
||||||
#: pg-act/ns/ns.php:16 pg-act/ns/srv.php:28 pg-act/ns/sshfp.php:25
|
#: pg-act/ns/ns.php:16 pg-act/ns/srv.php:28 pg-act/ns/sshfp.php:25
|
||||||
|
@ -825,6 +845,30 @@ msgstr ""
|
||||||
msgid "Stable Let's Encrypt certificates"
|
msgid "Stable Let's Encrypt certificates"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: pg-view/ht/keys.php:2
|
||||||
|
msgid "In addition to your password, you can also access your SFTP space using Ed25519 SSH keys. A key can be granted modification rights to the full space (<code>/</code>) or to any arbitrary subdirectory. A key is always allowed to list any directory content."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pg-view/ht/keys.php:17
|
||||||
|
msgid "Add new SSH key access"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pg-view/ht/keys.php:17
|
||||||
|
msgid "SSH key access"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pg-view/ht/keys.php:19
|
||||||
|
msgid "Public key"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pg-view/ht/keys.php:23
|
||||||
|
msgid "Allowed directory"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: pg-view/ht/keys.php:30
|
||||||
|
msgid "Update"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: pg-view/ns/caa.php:3
|
#: pg-view/ns/caa.php:3
|
||||||
msgid "Flag"
|
msgid "Flag"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -193,5 +193,10 @@ define('PAGES', [
|
||||||
'title' => _('Delete access'),
|
'title' => _('Delete access'),
|
||||||
'description' => _('Delete an existing HTTP access from a subdirectory of the SFTP space'),
|
'description' => _('Delete an existing HTTP access from a subdirectory of the SFTP space'),
|
||||||
],
|
],
|
||||||
|
'keys' => [
|
||||||
|
'title' => _('Manage SSH keys'),
|
||||||
|
'description' => _('Choose what SSH key can edit what directory'),
|
||||||
|
'tokens_account_cost' => 300,
|
||||||
|
],
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -26,4 +26,3 @@ $_SESSION['type'] = query('select', 'users', ['id' => $id], 'type')[0];
|
||||||
setupDisplayUsername($_POST['username']);
|
setupDisplayUsername($_POST['username']);
|
||||||
|
|
||||||
redir();
|
redir();
|
||||||
|
|
||||||
|
|
39
pg-act/ht/keys.php
Executable file
39
pg-act/ht/keys.php
Executable file
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$el_nb = count($_POST['keys']);
|
||||||
|
if ($el_nb < 1 OR $el_nb > 8)
|
||||||
|
output(403, 'Wrong elements number.');
|
||||||
|
|
||||||
|
foreach ($_POST['keys'] as $i => $key) {
|
||||||
|
if (($key['public-key'] ?? '') === '') {
|
||||||
|
unset($_POST['keys'][$i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (preg_match('#^/[/\p{L}\{M}\p{N}\p{P}\p{S}\p{Zs}]{1,254}$#Du', $key['dir'] ?? '') !== 1)
|
||||||
|
output(403, _('Path is not valid.'));
|
||||||
|
if (preg_match('#' . ED25519_PUBKEY_REGEX . '#D', $key['public-key']) !== 1)
|
||||||
|
output(403, _('Ed25519 public key seems wrongly formatted.'));
|
||||||
|
}
|
||||||
|
$keys = array_values($_POST['keys']);
|
||||||
|
|
||||||
|
rateLimit();
|
||||||
|
|
||||||
|
try {
|
||||||
|
DB->beginTransaction();
|
||||||
|
|
||||||
|
query('delete', 'ssh-keys', ['username' => $_SESSION['id']]);
|
||||||
|
|
||||||
|
foreach ($keys as $key)
|
||||||
|
insert('ssh-keys', [
|
||||||
|
'key' => $key['public-key'],
|
||||||
|
'username' => $_SESSION['id'],
|
||||||
|
'directory' => $key['dir'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
DB->commit();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
DB->rollback();
|
||||||
|
output(500, 'Database error.', [$e->getMessage()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
output(200, _('SSH keys updated.'));
|
31
pg-view/ht/keys.php
Executable file
31
pg-view/ht/keys.php
Executable file
|
@ -0,0 +1,31 @@
|
||||||
|
<p>
|
||||||
|
<?= _('In addition to your password, you can also access your SFTP space using Ed25519 SSH keys. A key can be granted modification rights to the full space (<code>/</code>) or to any arbitrary subdirectory. A key is always allowed to list any directory content.') ?>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form method="post">
|
||||||
|
<datalist id="dirs">
|
||||||
|
<option value="/"></option>
|
||||||
|
<?php
|
||||||
|
foreach (listFsDirs($_SESSION['id'] ?? '') as $dir)
|
||||||
|
echo ' <option value="/' . $dir . '"></option>' . LF;
|
||||||
|
?>
|
||||||
|
</datalist>
|
||||||
|
<?php
|
||||||
|
foreach (array_slice(array_merge(query('select', 'ssh-keys', ['username' => $_SESSION['id'] ?? '']), [['key' => '', 'username' => '', 'directory' => '/']]), 0, 8) as $i => $ssh_key) {
|
||||||
|
?>
|
||||||
|
<fieldset>
|
||||||
|
<legend><?= ($ssh_key['key'] === '') ? _('Add new SSH key access') : _('SSH key access') ?></legend>
|
||||||
|
<div>
|
||||||
|
<label for="public-key"><?= _('Public key') ?></label><br>
|
||||||
|
<code>ssh-ed15519 <input pattern="<?= ED25519_PUBKEY_REGEX ?>" placeholder="AAAAC3NzaC1lZDI1NTE5AAAAI<?= substr(base64_encode(random_bytes(32)), 0, 43) ?>" id="public-key" name="keys[<?= $i ?>][public-key]" value="<?= $ssh_key['key'] ?>" type="text"></code>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="dir"><?= _('Allowed directory') ?></label><br>
|
||||||
|
<input list="dirs" placeholder="/" value="<?= htmlspecialchars($ssh_key['directory']) ?>" id="dir" name="keys[<?= $i ?>][dir]" type="text">
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<input type="submit" value="<?= _('Update') ?>">
|
||||||
|
</form>
|
|
@ -1,4 +1,4 @@
|
||||||
<?php
|
<?php // ServNest authenticator for SFTPGo https://github.com/drakkan/sftpgo/blob/main/docs/external-auth.md
|
||||||
|
|
||||||
const DEBUG = false;
|
const DEBUG = false;
|
||||||
!DEBUG or ob_start();
|
!DEBUG or ob_start();
|
||||||
|
@ -21,27 +21,34 @@ $username = hashUsername($auth_data['username']);
|
||||||
if (usernameExists($username) !== true)
|
if (usernameExists($username) !== true)
|
||||||
deny('This username doesn\'t exist.');
|
deny('This username doesn\'t exist.');
|
||||||
|
|
||||||
if (!in_array('ht', explode(',', query('select', 'users', ['username' => $username], 'services')[0]), true))
|
$account = query('select', 'users', ['username' => $username])[0];
|
||||||
|
|
||||||
|
if (!in_array('ht', explode(',', $account['services']), true))
|
||||||
deny('Service not enabled for this user.');
|
deny('Service not enabled for this user.');
|
||||||
|
|
||||||
$id = query('select', 'users', ['username' => $username], 'id')[0];
|
const SFTPGO_DENY_PERMS = ['/' => ['list']];
|
||||||
|
const SFTPGO_ALLOW_PERMS = ['list', 'download', 'upload', 'overwrite', 'delete_files', 'delete_dirs', 'rename_files', 'rename_dirs', 'create_dirs', 'chtimes'];
|
||||||
|
if ($auth_data['password'] !== '') {
|
||||||
|
if (checkPassword($account['id'], $auth_data['password']) !== true)
|
||||||
|
deny('Wrong password.');
|
||||||
|
$permissions['/'] = SFTPGO_ALLOW_PERMS;
|
||||||
|
} else if ($auth_data['public_key'] !== '') {
|
||||||
|
$permissions = SFTPGO_DENY_PERMS;
|
||||||
|
foreach (query('select', 'ssh-keys', ['username' => $account['id']]) as $key)
|
||||||
|
if (hash_equals('ssh-ed25519 ' . $key['key'] . LF, $auth_data['public_key']))
|
||||||
|
$permissions[$key['directory']] = SFTPGO_ALLOW_PERMS;
|
||||||
|
if ($permissions === SFTPGO_DENY_PERMS)
|
||||||
|
deny('No matching SSH key allowed.');
|
||||||
|
} else
|
||||||
|
deny('Unknown authentication method.');
|
||||||
|
|
||||||
if (checkPassword($id, $auth_data['password']) !== true)
|
echo json_encode([
|
||||||
deny('Wrong password.');
|
'status' => 1,
|
||||||
|
'username' => $auth_data['username'],
|
||||||
|
'home_dir' => CONF['ht']['ht_path'] . '/fs/' . $account['id'],
|
||||||
|
'quota_size' => ($account['type'] === 'approved') ? CONF['ht']['user_quota_approved'] : CONF['ht']['user_quota_testing'],
|
||||||
|
'permissions' => $permissions,
|
||||||
|
], JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
|
||||||
|
|
||||||
echo '
|
!DEBUG or file_put_contents(ROOT_PATH . '/db/debug.txt', ob_get_contents() . 'accepted');
|
||||||
{
|
|
||||||
"status": 1,
|
|
||||||
"username": ' . json_encode($auth_data['username']) . ',
|
|
||||||
"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": {
|
|
||||||
"/": [
|
|
||||||
"*"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
';
|
|
||||||
|
|
||||||
!DEBUG or file_put_contents(ROOT_PATH . '/db/debug.txt', ob_get_contents());
|
|
||||||
http_response_code(200);
|
http_response_code(200);
|
||||||
|
|
Loading…
Reference in a new issue