Nginx + PHP-FPM: Neue Webseite automatisiert erstellen

Letztes Mal hatte ich euch gezeigt, wie man relativ einfach voneinander getrennte Auftritte mit Nginx und PHP-FPM bereitstellen kann. Eine Sache war euch jedoch sicherlich aufgefallen: Das Anlegen und Konfigurieren benötigt viele Schritte, die sich für jede neue Webseite wiederholen.

Und da es mir auch zu aufwändig ist, soetwas immer und immer wieder händisch zu erledigen, habe ich mir das ganze durch ein kleines Script automatisiert. Geschrieben habe ich es in PHP (ist sowieso auf dem System vorhanden und vor allem für solche Kleinigkeiten gut geeignet). 🙂

Ich habe das ganze in 3 Dateien aufgeteilt: Ein Script zum Anlegen von statischen Webseiten (ohne PHP), ein Script zum Anlegen von dynamischen Inhalten (mit PHP) und ein Script, in dem die ganzen Hilfsfunktionen enthalten sind. Solchen PHP-Scripten gebe ich immer die Endung .phs um sie nicht mit Webscripten vertauschen zu können. Sie alle habe ich in den Ordner "/var/www/" verfrachtet, da dort auch sämtliche Konfigurationsdateien abgelegt sind. Es reicht übrigens aus, wenn root die Dateien lesen und schreiben kann. 😉

Die eigentlichen Scripte sind ziemlich kurz geraten. Sie heißen bei mir "make_static.phs" und "make_php.phs" und werden einfach wie folgt aufgerufen:

1
2
sudo php /var/www/make_static.phs example.com
sudo php /var/www/make_php.phs example.net

"example.com" bzw. "example.net" müssen natürlich durch eure tatsächliche Domain ersetzt werden. Die Scripte kümmern sich dann entsprechend um das Anlegen der Konfigurationen, das Anlegen des entsprechenden Nutzers, das Anlegen der benötigten Ordner und das Neustarten der Services.

Hier die "make_static.phs":

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
  include("/var/www/make_inc.phs");

  $domain = init($argv);

  if ($domain != NULL) {
    $user = getUserFromDomain($domain);

    createUser($user, false);

    writeNginx($domain, $user, false);

    restartNginx();
  }

  finit($domain)
?>

Und hier die "make_php.phs":

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
  include("/var/www/make_inc.phs");

  $domain = init($argv);

  if ($domain != NULL) {
    $user = getUserFromDomain($domain);

    createUser($user, true);

    writeNginx($domain, $user, true);
    writePhp($user);

    restartNginx();
    restartPhp();
  }

  finit($domain)
?>

Und hier nun die "make_inc.phs":

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
<?php
  $HOME_PATH = "/home/sftpuser/";

  $MYSQL_SOCKET = "/var/mysql/socket/mysqld.socket";

  $NGINX_CONF = "/var/www/nginx/";
  $NGINX_PATH = "/etc/init.d/nginx";

  $PHP_CONF    = "/var/www/php-fpm/";
  $PHP_PATH    = "/etc/init.d/php5-fpm";
  $PHP_SOCKETS = "/var/www/sockets/";

  $LIBNSS_PATH    = "/lib/libnss_dns.so.2";
  $LOCALTIME_PATH = "/etc/localtime";
  $RESOLV_PATH    = "/etc/resolv.conf";
  $ZONEINFO_PATH  = "/usr/share/zoneinfo/";

  $CHMOD_CMD    = "chmod";
  $CHMOD_R_CMD  = "chmod -R";
  $CHOWN_CMD    = "chown";
  $CHOWN_R_CMD  = "chown -R";
  $CP_CMD       = "cp";
  $CP_R_CMD     = "cp -R";
  $GROUPMOD_CMD = "usermod -a -G";
  $HOMEMOD_CMD  = "usermod -d";
  $LN_CMD       = "ln";
  $MKDIR_CMD    = "mkdir";
  $MKNOD_CMD    = "mknod -m 666";
  $SHELLMOD_CMD = "usermod -s";
  $USERADD_CMD  = "useradd";

  $NOLOGIN_SHELL = "/usr/sbin/nologin";

  $ADMIN_USER = "[ADMIN]"; //!!! CHANGE THIS
  $NGINX_USER = "nginx";
  $PHP_USER   = "php-fpm";

  $HANDLE_FAVICON = true;
  $HANDLE_MYSQL   = true;
  $HANDLE_ROBOTS  = true;
  $HANDLE_SSL     = true;
  $HANDLE_UBUNTU  = true;
  $HANDLE_WWW     = true;

  function execute($command) {
    $return = "";

    print("\n> $command\n\n");

    system($command, $return);

    print("$return\n");
  }

  function addUserToGroup($user, $group) {
    global $GROUPMOD_CMD;

    execute("$GROUPMOD_CMD $group $user");
  }

  function createUser($user, $usePhp) {
    global $USERADD_CMD;

    global $ADMIN_USER;
    global $NGINX_USER;

    execute("$USERADD_CMD $user");

    addUserToGroup($ADMIN_USER, $user);
    addUserToGroup($NGINX_USER, $user);

    setUserHome($user);
    setUserShell($user);

    createUserHome($user, $usePhp);
  }

  function createUserHome($user, $usePhp) {
    global $HOME_PATH;

    global $CHMOD_CMD;
    global $CHMOD_R_CMD;
    global $CHOWN_CMD;
    global $CHOWN_R_CMD;
    global $CP_CMD;
    global $CP_R_CMD;
    global $LN_CMD;
    global $MKDIR_CMD;
    global $MKNOD_CMD;

    global $NGINX_USER;

    global $MYSQL_SOCKET;

    global $LIBNSS_PATH;
    global $LOCALTIME_PATH;
    global $RESOLV_PATH;
    global $ZONEINFO_PATH;

    global $HANDLE_MYSQL;
    global $HANDLE_UBUNTU;

    $folders = array();
    $folders[] = array($MKDIR_CMD => "",
                       $CHMOD_CMD => "1770",
                       $CHOWN_CMD => "$NGINX_USER:$user");
    $folders[] = array($MKDIR_CMD => "/docs",
                       $CHMOD_CMD => "1770",
                       $CHOWN_CMD => "$NGINX_USER:$user");
    $folders[] = array($MKDIR_CMD => "/home",
                       $CHMOD_CMD => "1700",
                       $CHOWN_CMD => "$user:$user");
    $folders[] = array($MKDIR_CMD => "/logs",
                       $CHMOD_CMD => "1750",
                       $CHOWN_CMD => "$NGINX_USER:$user");

    if ($usePhp) {
      $folders[] = array($MKDIR_CMD => "/bin",
                         $CHMOD_CMD => "1750",
                         $CHOWN_CMD => "$NGINX_USER:$user");
      $folders[] = array($MKDIR_CMD => "/dev",
                         $CHMOD_CMD => "1750",
                         $CHOWN_CMD => "$NGINX_USER:$user");
      $folders[] = array($MKDIR_CMD => "/etc",
                         $CHMOD_CMD => "1750",
                         $CHOWN_CMD => "$NGINX_USER:$user");
      $folders[] = array($MKDIR_CMD => "/lib",
                         $CHMOD_CMD => "1750",
                         $CHOWN_CMD => "$NGINX_USER:$user");
      $folders[] = array($MKDIR_CMD => "/sbin",
                         $CHMOD_CMD => "1750",
                         $CHOWN_CMD => "$NGINX_USER:$user");
      $folders[] = array($MKDIR_CMD => "/tmp",
                         $CHMOD_CMD => "1770",
                         $CHOWN_CMD => "$NGINX_USER:$user");
      $folders[] = array($MKDIR_CMD => "/usr",
                         $CHMOD_CMD => "1750",
                         $CHOWN_CMD => "$NGINX_USER:$user");

      $folders[] = array($MKDIR_CMD => "/usr/bin",
                         $CHMOD_CMD => "1750",
                         $CHOWN_CMD => "$NGINX_USER:$user");
      $folders[] = array($MKDIR_CMD => "/usr/lib",
                         $CHMOD_CMD => "1750",
                         $CHOWN_CMD => "$NGINX_USER:$user");
      $folders[] = array($MKDIR_CMD => "/usr/local",
                         $CHMOD_CMD => "1750",
                         $CHOWN_CMD => "$NGINX_USER:$user");
      $folders[] = array($MKDIR_CMD => "/usr/sbin",
                         $CHMOD_CMD => "1750",
                         $CHOWN_CMD => "$NGINX_USER:$user");

      $folders[] = array($MKDIR_CMD => "/usr/local/bin",
                         $CHMOD_CMD => "1750",
                         $CHOWN_CMD => "$NGINX_USER:$user");
      $folders[] = array($MKDIR_CMD => "/usr/local/etc",
                         $CHMOD_CMD => "1750",
                         $CHOWN_CMD => "$NGINX_USER:$user");
      $folders[] = array($MKDIR_CMD => "/usr/local/lib",
                         $CHMOD_CMD => "1750",
                         $CHOWN_CMD => "$NGINX_USER:$user");
      $folders[] = array($MKDIR_CMD => "/usr/local/sbin",
                         $CHMOD_CMD => "1750",
                         $CHOWN_CMD => "$NGINX_USER:$user");


      if ($HANDLE_MYSQL) {
        $folders[] = array($MKDIR_CMD => "/mysql",
                           $CHMOD_CMD => "1750",
                           $CHOWN_CMD => "$NGINX_USER:$user");
      }

      if ($HANDLE_UBUNTU) {
        $folders[] = array($MKDIR_CMD => "/usr/share",
                           $CHMOD_CMD => "1750",
                           $CHOWN_CMD => "$NGINX_USER:$user");
        $folders[] = array($MKDIR_CMD => "/usr/share/zoneinfo",
                           $CHMOD_CMD => "1750",
                           $CHOWN_CMD => "$NGINX_USER:$user");
      }
    }

    foreach ($folders as $folder) {
      execute("$MKDIR_CMD $HOME_PATH$user$folder[$MKDIR_CMD]");
      execute("$CHMOD_CMD $folder[$CHMOD_CMD] $HOME_PATH$user$folder[$MKDIR_CMD]");
      execute("$CHOWN_CMD $folder[$CHOWN_CMD] $HOME_PATH$user$folder[$MKDIR_CMD]");
    }

    if ($usePhp) {
      execute("$MKNOD_CMD $HOME_PATH$user/dev/null c 1 3");
      execute("$MKNOD_CMD $HOME_PATH$user/dev/random c 1 8");
      execute("$MKNOD_CMD $HOME_PATH$user/dev/urandom c 1 9");
      execute("$MKNOD_CMD $HOME_PATH$user/dev/zero c 1 5");
      execute("$CP_CMD $LIBNSS_PATH $HOME_PATH$user$LIBNSS_PATH");
      execute("$CP_CMD $RESOLV_PATH $HOME_PATH$user$RESOLV_PATH");

      if ($HANDLE_MYSQL) {
        execute("$LN_CMD $MYSQL_SOCKET $HOME_PATH$user/mysql/");
      }

      if ($HANDLE_UBUNTU) {
        execute("$CP_CMD $LOCALTIME_PATH $HOME_PATH$user$LOCALTIME_PATH");
        execute("$CP_R_CMD $ZONEINFO_PATH $HOME_PATH$user$ZONEINFO_PATH../");
      }
    }
  }

  function setUserHome($user) {
    global $HOME_PATH;

    global $HOMEMOD_CMD;

    execute("$HOMEMOD_CMD $HOME_PATH$user/home $user");
  }

  function setUserShell($user) {
    global $SHELLMOD_CMD;

    global $NOLOGIN_SHELL;

    execute("$SHELLMOD_CMD $NOLOGIN_SHELL $user");
  }

  function getUserFromDomain($domain) {
    $return = ereg_replace("[.]", "_", $domain);

    print("user  : $return\n");

    return $return;
  }

  function restartNginx() {
    global $NGINX_PATH;

    execute("$NGINX_PATH restart");
  }

  function restartPhp() {
    global $PHP_PATH;

    execute("$PHP_PATH restart");
  }

  function writeNginx($domain, $user, $usePhp) {
    global $HOME_PATH;

    global $CHMOD_CMD;
    global $CHOWN_CMD;

    global $NGINX_CONF;
    global $PHP_SOCKETS;

    global $NGINX_USER;

    global $HANDLE_FAVICON;
    global $HANDLE_ROBOTS;
    global $HANDLE_SSL;
    global $HANDLE_WWW;

    $config  = "server {\n";
    $config .= "  listen 80;\n";

    if ($HANDLE_SSL) {
      $config .= "  listen 443 ssl;\n";
    }

    $config .= "\n";

    if ($HANDLE_WWW) {
      $config .= "  server_name $domain www.$domain;\n";
      $config .= "\n";
      $config .= "  if (\$http_host = www.$domain) {\n";
      $config .= "    rewrite ^(.*)\$ \$scheme://$domain\$1 permanent;\n";
      $config .= "  }\n";
    } else {
      $config .= "  server_name $domain;\n";
    }

    $config .= "\n";
    $config .= "  access_log $HOME_PATH$user/logs/access.log;\n";
    $config .= "  error_log  $HOME_PATH$user/logs/error.log info;\n";
    $config .= "\n";
    $config .= "  root $HOME_PATH$user/docs/;\n";

    if ($usePhp) {
      $config .= "\n";
      $config .= "  # ENABLE PHP\n";
      $config .= "  location ~ ^(.*)\\.php\$ {\n";
      $config .= "    if (!-f \$request_filename) {\n";
      $config .= "      return 404;\n";
      $config .= "    }\n";
      $config .= "\n";
      $config .= "    include      /etc/nginx/fastcgi_params;\n";
      $config .= "    fastcgi_pass unix:$PHP_SOCKETS$user.socket;\n";
      $config .= "    #fastcgi_pass 127.0.0.1:10001;\n";
      $config .= "  }\n";
    }

    $config .= "\n";
    $config .= "  include $NGINX_CONF"."conf.default;\n";

    if ($HANDLE_FAVICON) {
      $config .= "\n";
      $config .= "  location = /favicon.ico {\n";
      $config .= "    if (!-f \$request_filename) {\n";
      $config .= "      return 204;\n";
      $config .= "    }\n";
      $config .= "  }\n";
    }

    if ($HANDLE_ROBOTS) {
      $config .= "\n";
      $config .= "  location = /robots.txt {\n";
      $config .= "    if (!-f \$request_filename) {\n";
      $config .= "      return 204;\n";
      $config .= "    }\n";
      $config .= "  }\n";
    }

    $config .= "\n";
    $config .= "  #deactivate later on\n";
    $config .= "  #autoindex on;\n";
    $config .= "}\n";

    file_put_contents("$NGINX_CONF$user.conf", $config);

    execute("$CHMOD_CMD 660 $NGINX_CONF$user.conf");
    execute("$CHOWN_CMD $NGINX_USER:$NGINX_USER $NGINX_CONF$user.conf");
  }

  function writePhp($user) {
    global $HOME_PATH;

    global $CHMOD_CMD;
    global $CHOWN_CMD;

    global $PHP_CONF;
    global $PHP_SOCKETS;

    global $PHP_USER;

    $config  = "[$user]\n";
    $config .= "listen                 = $PHP_SOCKETS$user.socket\n";
    $config .= ";listen                 = 127.0.0.1:10001\n";
    $config .= "listen.backlog         = -1\n";
    $config .= "listen.allowed_clients = 127.0.0.1\n";
    $config .= "listen.owner           = $user\n";
    $config .= "listen.group           = $user\n";
    $config .= "listen.mode            = 0660\n";
    $config .= "\n";
    $config .= "user  = $user\n";
    $config .= "group = $user\n";
    $config .= "\n";
    $config .= "pm                   = dynamic\n";
    $config .= "pm.max_children      = 5\n";
    $config .= "pm.start_servers     = 2\n";
    $config .= "pm.min_spare_servers = 1\n";
    $config .= "pm.max_spare_servers = 3\n";
    $config .= "pm.max_requests      = 500\n";
    $config .= "\n";
    $config .= "chroot = $HOME_PATH$user\n";
    $config .= "chdir  = /docs\n";
    $config .= "\n";
    $config .= "env[HOSTNAME]      = \$HOSTNAME\n";
    $config .= "env[PATH]          = /usr/local/bin:/usr/bin:/bin\n";
    $config .= "env[TMP]           = /tmp\n";
    $config .= "env[TMPDIR]        = /tmp\n";
    $config .= "env[TEMP]          = /tmp\n";
    $config .= "env[HOME]          = /home\n";
    $config .= "env[DOCUMENT_ROOT] = /docs\n";

    file_put_contents("$PHP_CONF$user.conf", $config);

    execute("$CHMOD_CMD 660 $PHP_CONF$user.conf");
    execute("$CHOWN_CMD $PHP_USER:$PHP_USER $PHP_CONF$user.conf");
  }

  function init($arguments) {
    $return = NULL;

    if (count($arguments) > 1) {
      $return = strtolower(ereg_replace("[^A-Za-z0-9.-]", "", $arguments[1]));

      if (strlen($return) > 0) {
        print("domain: $return\n");
      } else {
        $return = NULL;
      }
    }

    return $return;
  }

  function finit($domain) {
    if ($domain != NULL) {
    } else {
      print("ERROR: domain must not be empty\n");
    }
  }
?>

Der "make_inc.phs" solltet ihr euch ein paar Minuten widmen, da dort ein paar Einstellungen auf euch warten. Die Pfadangaben sollten verständlich sein: Wo finden sich die Init-Scripte, wie lauten die User-Homeverzeichnisse, wo befinden sich die Configs von Nginx und PHP-FPM und wo werden Socket-Dateien abgelegt.
Spannend dürften auch die User-Namen sein. Klar, für jede Webseite wird ein neuer Nutzer angelegt, dessen Name die entsprechende Domain widerspiegelt. Sowohl der Admin (von euch zu ändern!) als auch Nginx werden dessen Nutzergruppe hinzugefügt, um die Dateien erreichen zu können.
Abschließend gibt es noch "HANDLE_FAVICON", "HANDLE_SSL" und "HANDLE_WWW". Ersteres sorgt dafür, dass Browser beim Aufrufen eines nicht-existierenden Favicons keinen 404 erhalten, zweiteres dass der Server SSL bereitstellt und drittes sorgt dafür, dass durch Rewriting vorangestellte "www."-Subdomains verschwinden.

Noch eine Info: In den erstellten Nginx-Konfigurationsdateien wird die Datei "/var/www/nginx/conf.default" inkludiert. In diese habe ich Regeln ausgelagert, die sich bei wirklich jeder Webseite wiederholen. In meinem Fall ist das z.b. das Nichtausliefern von versteckten Dateien und Ordnern:

1
2
3
4
5
rewrite ^(.*)\/\.(.*)$ @404 break;

location = @404 {
  return 404;
}

Ihr werdet es schon gemerkt haben: Diese Scripte sind sehr stark auf meine eigenen Bedürfnisse zugeschnitten. Ja, nicht jeder will seinen Server derart aufsetzen - aber evtl. erhalten ja auch andere Leute ein paar Ideen durch diese Beispielimplementierung. 🙂

Und wie immer gilt: Ich hafte nicht für Schäden an Software, Hardware oder für Vermögensschäden, die durch Anwendung dieser Änderungen entstanden sind oder entstehen könnten. 😉
Update:
Das Script wurde an den aktuellen Stand angepasst. Primär sind die Schalter "HANDLE_MYSQL", "HANDLE_ROBOTS" und "HANDLE_UBUNTU" hinzugekommen. HANDLE_MYSQL erzeugt einen Hardlink zum mysqld-Socket im Verzeichnis des entsprechenden Nutzers. HANDLE_ROBOTS arbeitet wie HANDLE_FAVICON und erzeugt eine Regel, durch die bei fehlender "robots.txt" ein 204-Code zurückgeliefert wird. HANDLE_UBUNTU sorgt dafür, dass im PHP Chroot alle Dateien vorliegen, damit PHP ordentlich funktionieren kann. Im speziellen wird dadurch folgender Fehler gelöst:

1
Fatal error: main(): Timezone database is corrupt - this should *never* happen! in [...] on line [...]

Update:
Das Script ist wieder einmal erweitert worden. Die für PHP eingerichtete chroot-Umgebung reicht nun aus, um DNS-Abfragen auszuführen ("/etc/resolv.conf" und "/lib/libnss_dns.so.2") und verschlüsselte Verbindungen herzustellen ("/dev/random" und "/dev/urandom").
Automatisierte Grüße, Kenny

8 Kommentare » Schreibe einen Kommentar

  1. Hey, danke erstmal für das Script. Funktioniert soweit super. Allerdings habe ich anscheinend Probleme beim Linken des MySQL-sockets:

    > ln /run/mysqld/mysqld.sock /var/www/test/mysql/

    ln: Die harte Verknüpfung „/var/www/test/mysql/mysqld.sock“ => „/run/mysqld/mysqld.sock“ konnte nicht angelegt werden: Ungültiger Link über Gerätegrenzen hinweg.

    Weisst du da einen Ausweg?

    • Hallo, das klingt, als ob die Verzeichnisse "/var/www/test/mysql/" und "/run/mysqld/" bei dir auf zwei verschiedenen Partitionen liegen. Hardlinks können aber nur auf ein und derselben Partition angelegt werden.

  2. Servus,

    ich habe das Script für die Einbindung von Graphicsmagick und Ghostscript erweitert. Hast Du interesse daran? Würde dir das gerne zur Verfügung stellen. Läuft beides im Chroot im Nginx, brauchte ich für eine Typo3-Webseite 🙂

    Liebe Grüße

  3. Servus,

    ich habe mir all deine NGinX-Beiträge mal angeschaut und ich habe ein komplett ähnliches Setup. Deine Scripte zum automatischen Erstellen von Webseiten gefallen mir und ich war so frei und habe dieses ein wenig angepasst, was ich Dir hier mitteilen möchte, denn dein Setup kann man noch verbessern 🙂

    1. Du chrootest die Nutzer ja auch in ihre Verzeichnisse. Mit deinem jetzigen Setup kann ein Nutzer, welcher sich einloggt, jedoch alle anderen "Domains" oder Nutzerverzeichnisse sehen. Das ist suboptimal, denn das will man ja mittels chroot verhindern. Folgende Änderungen sind hier vorzunehmen (ich nutze /srv/kunden als Rootverzeichnis):

    ##### /etc/ssh/sshd_config #####
    (Hier den Kunden nicht ins Webroot für alle, sondern in sein eigenes Verzeichnis sperren)

    - ChrootDirectory /srv/kunden/%u

    ##### make_inc.phs #####

    - Variable für SFTPNutzer-Gruppe einfügen (hier $KUNDEN = "kunden";

    - In der Funktion createUser() den Domainnutzer der SFTP-Nutzergruppe hinzufügen addUserToGroup($user, $KUNDEN);

    - Damit CHROOT funktioniert müssen die Verzeichnisrechte dem Nutzer root gehören, was aber auch i.O. ist, denn schreiben soll der Nutzer ja garnicht in das CHROOT Rootverzeichnis

    $folders[] = array($MKDIR_CMD => "",
    $CHMOD_CMD => "1750", // Statt 1770 hier 1750 da nur Execute nicht schreiben
    $CHOWN_CMD => "$PHP_USER:$user"); // Statt $NGINX_USER hier root (ist bei mir $PHP_USER)

    - In der Funktion writeNginx() sollte man, sofern man in Nginx sites-enabled UND sites-available nutzt hier noch differenzieren und einen Link hinzufügen ($NGINX_ENABLED_CONF zeigt bei mir auf /etc/nginx/sites-enabled/)

    execute("$LN_CMD $NGINX_CONF$user.conf $NGINX_ENABLED_CONF$user.conf");

    Damit hast Du folgendes erreicht:

    1. Nutzer können keinen anderen "geChrooteten"-Verzeichnisse sehen
    2. Jeder Nutzer ist _wirklich_ in seinem _eigenen_ Chroot ohne Schreibrechte in /
    3. Bei erstellen einer neuen Domain wird der Nutzer gleich der SFTP-Gruppe hinzugefügt

    Man kann / sollte ggf. noch ein ARGV einbauen um automatisch ein Passwort mitzugeben, sonst muss man das immer nachträglich machen 😉

    LG

    Florian

    • Hallo Florian, vielen Dank für deine Verbesserungsvorschläge. 🙂

      Der gemeinsame Einstiegsordner für alle SFTP-Nutzer war wirklich eine (eher unschöne) Lösung. Ich hatte dort das Problem, dass der SFTP-Login nicht funktionierte, wenn ich versuchte, die Nutzer direkt in ihr tatsächliches Verzeichnis zu sperren. Mag daran gelegen haben, dass ich mit eigenen Nutzern gearbeitet habe und nicht auf root zurückgreifen wollte.

      sites-available und sites-enabled verwende ich persönlich nicht (wie man an der Config unschwer erkennen kann). Ich habe darin bisher keinen echten Mehrwert für mich persönlich gesehen.

      Ich hatte absichtlich das automatische Hinzufügen zur SFTP-Gruppe weggelassen. Hintergrund: Ich verwende die Scripte für meinen eigenen Server. Dort existiert ein übergeordneter Nutzer, der in allen Domain-spezifischen Gruppen ist. So erspare ich mir es, für jede Domain separat einen Login zu pflegen. Der SFTP-Zugang kommt dann zum Einsatz, wenn wirklich von extern Informationen bereitstellt werden sollen. Aus dem gleichen Grund gibt's auch von vorneherein kein Passwort, da die Domain-spezifischen Nutzer sich nur in diesen Sonderfällen remote einloggen können sollen - in denen es für mich ausreicht, das Passwort manuell zu setzen. 🙂

      Ich finde es aber schön, dass die Scripte tatsächlich auch weitere Verwendung finden. 🙂

  4. Nur mal so ne Frage. Warum nicht eine Datei, die Aktion anhand der Argumente erledigen und den Interpreter oben angeben? Dann kannst Du Dir den extra Aufruf von PHP sparen. (Ok, ich hätte es wahrscheinlich direkt in bash gemacht.)

    • Ob ich da nun noch PHP vor den Aufruf schreiben muss oder nicht, das ist mir ziemlich egal. Klar, ich hätte noch ordentliche Parameterisierung etc. einbauen können, aber einen wirklichen Mehrwert bietet es mir nicht. 🙂

      Ich bevorzuge PHP, da es IMHO wesentlich ausdrucksstärker ist - das spiegelt sich dann in der Wartbarkeit des Codes wider. 😀

Schreibe einen Kommentar

Um Ihnen beim weiteren Kommentieren auf dieser Webseite die erneute Eingabe Ihrer Daten zu ersparen, wird beim Absenden Ihres Kommentars ein Cookie an Ihren Browser gesendet und von diesem gespeichert. Mit dem Absenden eines Kommentars auf dieser Webseite stimmen Sie der Speicherung und Übertragung dieses Cookies explizit zu.

Pflichtfelder sind mit * markiert.