34 Commits
0.1 ... master

Author SHA1 Message Date
  Björn 95be8c61b0 adding 4 years ago
  Björn 892c7ac130 adding #45 4 years ago
  Björn 95d748254e adding #45 4 years ago
  Björn 4fb3cd81a6 adding #45 4 years ago
  Björn 8b99447b31 adding #45 4 years ago
  Björn 624983950e adding #45 4 years ago
  Björn e1f1ec3d8c adding #45 4 years ago
  Björn de3a1feba3 adding #45 4 years ago
  Björn bb342dd389 adding #45 4 years ago
  Björn edef5733b7 adding #45 4 years ago
  Björn 5a53c9a07f adding #45 4 years ago
  Björn c7b6430dca adding #45 4 years ago
  Björn 613e1c8cad adding #45 4 years ago
  Björn 63625cfea8 adding #45 4 years ago
  Björn 11806d2cfd adding #45 4 years ago
  Björn fc523207af adding #45 4 years ago
  Björn 93556a99a7 adding #25 4 years ago
  Björn 9b60d5cc3b adding #25 4 years ago
  Björn 5f45a5b02b adding #42 4 years ago
  Björn 0f11e6d5fe adding #44 4 years ago
  Björn 886a5a2279 adding 4 years ago
  Björn 8349a397aa adding #34 4 years ago
  Björn 63e12a28e9 adding #34 4 years ago
  Björn f09af98a4a adding #34 4 years ago
  Björn df7645c21e adding #34 4 years ago
  Björn 8d7520090b readme 4 years ago
  Björn 829f4bd231 adding #35 4 years ago
  Björn e19a5d617e adding #38 4 years ago
  Björn 83e99fd066 adding #39 4 years ago
  Björn b64343012a adding #32 4 years ago
  Björn 2f85fa4520 adding #40 4 years ago
  Björn f424d0aca6 adding #41 4 years ago
  Björn d738c7622e adding #36 4 years ago
  Björn 2e489f4470 adding #37 4 years ago
24 changed files with 754 additions and 185 deletions
Split View
  1. +1
    -1
      README.md
  2. +54
    -0
      app/Commands/AdminerInstallCommand.php
  3. +2
    -2
      app/Commands/Fail2banDisableCommand.php
  4. +2
    -2
      app/Commands/Fail2banEnableCommand.php
  5. +123
    -0
      app/Commands/Fail2banManageCommand.php
  6. +18
    -16
      app/Commands/LetsEncryptAddCommand.php
  7. +2
    -5
      app/Commands/LetsEncryptInstallCommand.php
  8. +2
    -2
      app/Commands/LetsEncryptRemoveCommand.php
  9. +23
    -33
      app/Commands/MariadbAddCommand.php
  10. +105
    -0
      app/Commands/MariadbClientInstallCommand.php
  11. +82
    -28
      app/Commands/MariadbInstallCommand.php
  12. +1
    -2
      app/Commands/NginxInstallCommand.php
  13. +7
    -7
      app/Commands/NodejsInstallCommand.php
  14. +14
    -12
      app/Commands/PhpFpmInstallCommand.php
  15. +28
    -1
      app/Factories/NginxVhostFactory.php
  16. +2
    -2
      app/Helpers/InstallHelper.php
  17. +40
    -8
      app/Helpers/NginxVhostHelper.php
  18. +122
    -0
      app/Menus/ItemValidator.php
  19. +32
    -0
      app/Menus/Nginx/NginxVhostCancelAction.php
  20. +1
    -2
      app/Menus/Nginx/NginxVhostGoBackAction.php
  21. +81
    -51
      app/Menus/Nginx/TemplateMenuFactory.php
  22. +6
    -0
      resources/fail2ban/jail.d/mysql-auth.conf
  23. +5
    -0
      resources/nginx/templates/empty.blade.php
  24. +1
    -11
      resources/nginx/templates/layouts/ssl.blade.php

+ 1
- 1
README.md View File

@ -1,6 +1,6 @@
# MCP
**Version: 0.1-Alpha**
**Version: 0.3**
* Nginx
* Lets Encrypt


+ 54
- 0
app/Commands/AdminerInstallCommand.php View File

@ -0,0 +1,54 @@
<?php
namespace App\Commands;
use Illuminate\Console\Scheduling\Schedule;
use LaravelZero\Framework\Commands\Command;
use App\Facades\Install;
/**
* Install php-fpm
*
* @author Björn Hase, Tentakelfabrik
* @license http://opensource.org/licenses/MIT The MIT License
* @link https://gitea.tentakelfabrik.de/Tentakelfabrik/mcp
*
*/
class AdminerInstallCommand extends Command
{
/**
* The signature of the command.
*
* @var string
*/
protected $signature = 'adminer:install {destination}';
/**
* The description of the command.
*
* @var string
*/
protected $description = 'Install Adminer.';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->info('Adminer installing...');
// download adminer
system('wget "http://www.adminer.org/latest.php" -O '.$this->argument('destination').'/index.php');
// check if nginx is ready and installed
if (file_exists($this->argument('destination').'/index.php')) {
$this->info('Adminer installing...Success! \o/');
} else {
$this->error('Failed! Please check log-file!');
}
}
}

+ 2
- 2
app/Commands/Fail2banDisableCommand.php View File

@ -12,7 +12,7 @@ use LaravelZero\Framework\Commands\Command;
* @author Björn Hase, Tentakelfabrik
* @license http://opensource.org/licenses/MIT The MIT License
* @link https://gitea.tentakelfabrik.de/Tentakelfabrik/mcp
*
*
*/
class Fail2banDisableCommand extends Command
{
@ -56,7 +56,7 @@ class Fail2banDisableCommand extends Command
unlink(self::DESTINATION_FAIL2BAN_JAIL_DIRECTORY.'/'.$configuration.'.conf');
exec('service fail2ban restart');
system('service fail2ban restart');
$this->info('Fail2ban...'.$configuration.' disabled');
}
}

+ 2
- 2
app/Commands/Fail2banEnableCommand.php View File

@ -12,7 +12,7 @@ use LaravelZero\Framework\Commands\Command;
* @author Björn Hase, Tentakelfabrik
* @license http://opensource.org/licenses/MIT The MIT License
* @link https://gitea.tentakelfabrik.de/Tentakelfabrik/mcp
*
*
*/
class Fail2banEnableCommand extends Command
{
@ -63,7 +63,7 @@ class Fail2banEnableCommand extends Command
}
copy($source, $destination);
exec('service fail2ban restart');
system('service fail2ban restart');
$this->info('Fail2ban...'.$configuration.' enabled');
}


+ 123
- 0
app/Commands/Fail2banManageCommand.php View File

@ -0,0 +1,123 @@
<?php
namespace App\Commands;
use Illuminate\Console\Scheduling\Schedule;
use LaravelZero\Framework\Commands\Command;
use PhpSchool\CliMenu\CliMenu;
use PhpSchool\CliMenu\MenuItem\CheckboxItem;
use PhpSchool\CliMenu\Builder\SplitItemBuilder;
use App\Facades\Menus\StylesFactory;
/**
* Manage Fail2ban Configuration
*
*
* @author Björn Hase, Tentakelfabrik
* @license http://opensource.org/licenses/MIT The MIT License
* @link https://gitea.tentakelfabrik.de/Tentakelfabrik/mcp
*
*/
class Fail2banManageCommand extends Command
{
// destination to jail
const DESTINATION_FAIL2BAN_JAIL_DIRECTORY = '/etc/fail2ban/jail.d';
// source to jail
const SOURCE_FAIL2BAN_JAIL_DIRECTORY = '/resources/fail2ban/jail.d';
// ignore files
const IGNORE_FILES = [
'.', '..', 'defaults-debian.conf'
];
// configuration
private $configuration = [];
// enabled
private $enabled = [];
/**
* The signature of the command.
*
* @var string
*/
protected $signature = 'fail2ban:manage';
/**
* The description of the command.
*
* @var string
*/
protected $description = 'manage fail2ban configuration';
/**
*
* @param [type] $file [description]
* @return boolean [description]
*/
private function name($file)
{
return str_replace('.conf', '', $file);
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
foreach(scandir(self::DESTINATION_FAIL2BAN_JAIL_DIRECTORY) as $file) {
if (!in_array($file, self::IGNORE_FILES)) {
$this->enabled[] = $this->name($file);
}
}
foreach(scandir(base_path().self::SOURCE_FAIL2BAN_JAIL_DIRECTORY) as $file) {
if (!in_array($file, self::IGNORE_FILES)) {
$name = $this->name($file);
$this->configuration[$name] = in_array($name, $this->enabled);
}
}
// create menu
$builder = $this->menu('Fail2ban');
foreach($this->configuration as $name => $single) {
// create checkbox
$checkbox = new CheckboxItem($name, function(CliMenu $menu) use ($name) {
if ($this->configuration[$name] === true) {
$this->call('fail2ban:disable', [ 'configuration' => $name ]);
$menu->redraw();
$menu->confirm($name.' is disabled!')->display('OK!');
} else {
$this->call('fail2ban:enable', [ 'configuration' => $name ]);
$menu->redraw();
$menu->confirm($name.' is enabled!')->display('OK!');
}
// getting new value
$this->configuration[$name] = $menu->getSelectedItem()->getChecked();
$menu->redraw();
});
// set default value
if ($this->configuration[$name]) {
$checkbox->setChecked(true);
}
$builder->addMenuItem($checkbox);
}
// apperance
$builder = StylesFactory::setMenuStyles($builder);
$builder->addLineBreak('-');
$mainmenu = $builder->build();
$mainmenu->open();
}
}

+ 18
- 16
app/Commands/LetsEncryptAddCommand.php View File

@ -5,6 +5,9 @@ namespace App\Commands;
use Illuminate\Console\Scheduling\Schedule;
use LaravelZero\Framework\Commands\Command;
use Respect\Validation\Validator as v;
use Respect\Validation\Exceptions\NestedValidationException;
/**
* Add LetsEncrypt Certificate
*
@ -21,7 +24,7 @@ class LetsEncryptAddCommand extends Command
*
* @var string
*/
protected $signature = 'lets-encrypt:add {email} {domain*} ';
protected $signature = 'lets-encrypt:add {email} {domain}';
/**
* The description of the command.
@ -38,7 +41,14 @@ class LetsEncryptAddCommand extends Command
*/
public function handle()
{
$domains = $this->argument('domain');
$email = $this->argument('email');
if (!v::email()->validate($email)) {
$this->error('First argument has to be a valid E-Mail! Failed!');
exit();
}
$domain = $this->argument('domain');
// adding flags
$domainFlags = '';
@ -46,21 +56,13 @@ class LetsEncryptAddCommand extends Command
// add file for domain
$saved = [];
// create flags
foreach($domains as $domain) {
$domainFlags .= '-d '.$domain.' ';
$saved[$domain] = '/etc/letsencrypt/live/'.$domain.'/fullchain.pem';
}
exec('certbot --non-interactive --agree-tos -m '.$this->argument('email').' --nginx '.$domainFlags, $output);
$this->line(implode("\n", $output));
system('certbot --non-interactive --agree-tos -m '.$this->argument('email').' --nginx -d '.$domain);
foreach($saved as $domain => $file) {
if (file_exists($file)) {
$this->info($domain.'...Success!');
} else {
$this->error($domain.'...Failed!');
}
// check for certificate
if (file_exists( '/etc/letsencrypt/live/'.$domain.'/fullchain.pem')) {
$this->info($domain.'...Success!');
} else {
$this->error($domain.'...Failed!');
}
}
}

+ 2
- 5
app/Commands/LetsEncryptInstallCommand.php View File

@ -50,7 +50,7 @@ class LetsEncryptInstallCommand extends Command
$output = [];
$this->info('LetsEncrypt adding ppa for certbot...');
exec('add-apt-repository -y universe 2>&1', $output);
exec('add-apt-repository -y ppa:certbot/certbot 2>&1', $output);
exec('add-apt-repository -y -r ppa:certbot/certbot 2>&1', $output);
// @TODO apt add a Warning for no good, in a later version output will be scanned for helpfull infos
$this->line(implode("\n", Install::filterAptMessages($output)));
@ -65,10 +65,7 @@ class LetsEncryptInstallCommand extends Command
$output = [];
$this->info('LetsEncrypt generate dhparam.pem...');
exec('openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096', $ouput);
// @TODO apt add a Warning for no good, in a later version output will be scanned for helpfull infos
$this->line(implode("\n", Install::filterAptMessages($output)));
system('openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096', $ouput);
if (Install::isReady('certbot')) {
$this->info('LetsEncrypt installing...Success! \o/');


+ 2
- 2
app/Commands/LetsEncryptRemoveCommand.php View File

@ -63,9 +63,9 @@ class LetsEncryptRemoveCommand extends Command
$errors++;
} else {
if (is_file($directory)) {
exec('rm '.$directory.' 2>&1', $output);
system('rm '.$directory.' 2>&1');
} else {
exec('rm -rf '.$directory.' 2>&1', $output);
system('rm -rf '.$directory.' 2>&1');
}
}
}


+ 23
- 33
app/Commands/MariadbAddCommand.php View File

@ -21,21 +21,15 @@ use Hackzilla\PasswordGenerator\RandomGenerator\Php7RandomGenerator;
*/
class MariadbAddCommand extends Command
{
// destination for username and password
const MCP_LOG_FILE = '/root/mcp.log';
// length for password
const NAME_LENGTH = 15;
// length for password
const PASSWORD_LENGTH = 40;
/**
* The signature of the command.
*
* @var string
*/
protected $signature = 'mariadb:add {--ssl}';
protected $signature = 'mariadb:add';
/**
* The description of the command.
@ -52,7 +46,7 @@ class MariadbAddCommand extends Command
public function handle()
{
// enter root password
$password = $this->secret('Enter root password');
$password = $this->secret('Root Password');
// connect database
try {
@ -64,18 +58,20 @@ class MariadbAddCommand extends Command
$this->info('Mariadb Create Database...');
// generate password
$generator = new ComputerPasswordGenerator();
$generator->setRandomGenerator(new Php7RandomGenerator())
->setUppercase()
->setLowercase()
->setUppercase()
->setNumbers()
->setSymbols(false)
->setLength(self::PASSWORD_LENGTH);
// setting password for root, repeat until
do {
$password = $this->secret('New Password');
$passwordRepeat = $this->secret('Repeat Password');
// getting password
$password = $generator->generatePasswords(1)[0];
if ($password !== $passwordRepeat) {
$this->error('Password not equal! Try again!');
}
if (empty($password)) {
$this->error('Password is empty! Try again!');
}
} while ($password !== $passwordRepeat || empty($password));
$generator = new ComputerPasswordGenerator();
$generator->setRandomGenerator(new Php7RandomGenerator())
@ -92,17 +88,14 @@ class MariadbAddCommand extends Command
$database = 'db'.$names[0];
$username = 'u'.$names[1];
// getting option for ssl
$ssl = $this->option('ssl');
$this->info('Database: '.$database);
$this->info('Username: '.$username);
$mysqli->query("CREATE DATABASE $database DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci");
if ($ssl === false) {
$mysqli->query("CREATE USER $username@localhost IDENTIFIED BY '$password'");
} else {
$mysqli->query("CREATE USER $username@'%' IDENTIFIED BY '$password'");
}
// create user for remote and local access
$mysqli->query("CREATE USER $username@'localhost' IDENTIFIED BY '$password'");
$mysqli->query("CREATE USER $username@'%' IDENTIFIED BY '$password'");
if ($mysqli->error) {
$this->error('Failed! '.$mysqli->error);
@ -113,11 +106,9 @@ class MariadbAddCommand extends Command
exit();
}
// grant privleges, for remote acces require ssl
$mysqli->query("GRANT ALL PRIVILEGES ON $database.* TO $username@localhost");
if ($ssl === true) {
$mysqli->query("GRANT ALL PRIVILEGES ON $database.* TO $username@'%' require ssl");
}
$mysqli->query("GRANT ALL PRIVILEGES ON $database.* TO $username@'%' require SSL");
if ($mysqli->error) {
$this->error('Failed! '.$mysqli->error);
@ -131,7 +122,6 @@ class MariadbAddCommand extends Command
$mysqli->query("FLUSH PRIVILEGES");
$mysqli->close();
$this->info('Success! \o/ Check /root/mcp.log');
file_put_contents(self::MCP_LOG_FILE, "Mariadb $database created\nuser: $username\npassword: $password\n--\n", FILE_APPEND);
$this->info('Success! \o/');
}
}

+ 105
- 0
app/Commands/MariadbClientInstallCommand.php View File

@ -0,0 +1,105 @@
<?php
namespace App\Commands;
use Illuminate\Console\Scheduling\Schedule;
use LaravelZero\Framework\Commands\Command;
use Illuminate\Support\Facades\File;
use App\Facades\Install;
use Hackzilla\PasswordGenerator\Generator\ComputerPasswordGenerator;
use Hackzilla\PasswordGenerator\RandomGenerator\Php7RandomGenerator;
/**
* Install Mariadb Client for Remote Access
*
* @author Björn Hase, Tentakelfabrik
* @license http://opensource.org/licenses/MIT The MIT License
* @link https://gitea.tentakelfabrik.de/Tentakelfabrik/mcp
*
*/
class MariadbClientInstallCommand extends Command
{
/**
* The signature of the command.
*
* @var string
*/
protected $signature = 'mariadb-client:install {remote_user} {remote_host} {version=10.4}';
/**
* The description of the command.
*
* @var string
*/
protected $description = 'Install Mariadb Client and set configuration';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->info('Mariadb Client install...');
$version = $this->argument('version');
exec('apt update 2>&1', $output);
// @TODO apt add a Warning for no good, in a later version output will be scanned for helpfull infos
$this->line(implode("\n", Install::filterAptMessages($output)));
if ($version === '10.4') {
$this->info('Mariadb try install 10.04...');
// getting release
$release = Install::getDistributionRelease();
if (Install::getDistributionId() === 'Ubuntu' && ($release === '18.04' || $release === '20.04')) {
$this->info('Mariadb install for Ubuntu '.$release.'...');
$output = [];
exec('apt install -y software-properties-common 2>&1', $output);
exec('apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xF1656F24C74CD1D8 2>&1', $output);
exec('add-apt-repository -y "deb [arch=amd64,arm64,ppc64el] http://mariadb.mirror.liquidtelecom.com/repo/10.4/ubuntu '.Install::getDistributionCodename().' main" 2>&1', $output);
exec('apt update 2>&1', $output);
}
}
exec('apt install -y mariadb-client 2>&1', $output);
// @TODO apt add a Warning for no good, in a later version output will be scanned for helpfull infos
$this->line(implode("\n", Install::filterAptMessages($output)));
if (Install::isReady('mariadb-client')) {
if (!is_dir('/etc/mysql/ssl')) {
system('mkdir /etc/mysql/ssl');
}
// getting
system('rsync -rv --include="ca-cert.pem" --include="client-cert.pem" --include="client-key.pem" --exclude="*" '.$this->argument('remote_user').'@'.$this->argument('remote_host').':/etc/mysql/ssl/ /etc/mysql/ssl/');
// checking if certificates are exists from remote server
if (!file_exists('/etc/mysql/ssl/ca-cert.pem') || !file_exists('/etc/mysql/ssl/client-cert.pem') || !file_exists('/etc/mysql/ssl/client-key.pem')) {
$this->error('Failed! Certificates not found!');
exit();
}
system('cat >> /etc/mysql/my.cnf << EOF
[client]
ssl-ca=/etc/mysql/ssl/ca-cert.pem
ssl-cert=/etc/mysql/ssl/client-cert.pem
ssl-key=/etc/mysql/ssl/client-key.pem
ssl');
system('chown -R mysql:mysql /etc/mysql/ssl');
system('chmod 644 /etc/mysql/ssl/*cert*');
system('chmod 644 /etc/mysql/ssl/*key*');
} else {
$this->error('Failed! Please check log-file!');
}
}
}

+ 82
- 28
app/Commands/MariadbInstallCommand.php View File

@ -21,18 +21,12 @@ use Hackzilla\PasswordGenerator\RandomGenerator\Php7RandomGenerator;
*/
class MariadbInstallCommand extends Command
{
// destination for username and password
const MCP_LOG_FILE = '/root/mcp.log';
// length for password
const PASSWORD_LENGTH = 40;
/**
* The signature of the command.
*
* @var string
*/
protected $signature = 'mariadb:install {version=10.4}';
protected $signature = 'mariadb:install {version=10.4} {--remote}';
/**
* The description of the command.
@ -79,51 +73,111 @@ class MariadbInstallCommand extends Command
// @TODO apt add a Warning for no good, in a later version output will be scanned for helpfull infos
$this->line(implode("\n", Install::filterAptMessages($output)));
if (Install::isReady('mariadb-server mariadb-client')) {
if (Install::isReady('mariadb-server') && Install::isReady('mariadb-client')) {
$this->info('Mariadb setup...');
// generate password
$generator = new ComputerPasswordGenerator();
$generator->setRandomGenerator(new Php7RandomGenerator())
->setUppercase()
->setLowercase()
->setUppercase()
->setNumbers()
->setSymbols(false)
->setLength(self::PASSWORD_LENGTH);
// setting password for root, repeat until
do {
$password = $this->secret('Root Password');
$passwordRepeat = $this->secret('Repeat Root Password');
if ($password !== $passwordRepeat) {
$this->error('Password not equal! Try again!');
}
// getting password for root
$password = $generator->generatePasswords()[0];
if (empty($password)) {
$this->error('Password is empty! Try again!');
}
} while ($password !== $passwordRepeat || empty($password));
// make sure root can only access from local
$this->info('Mariadb make sure root can ony access from local...');
exec('sudo mysql -u root -e "DELETE FROM mysql.user WHERE User=\'root\' AND Host NOT IN (\'localhost\', \'127.0.0.1\', \'::1\');"');
system('sudo mysql -u root -e "DELETE FROM mysql.user WHERE User=\'root\' AND Host NOT IN (\'localhost\', \'127.0.0.1\', \'::1\');"');
// delete anonymous user
$this->info('Mariadb delete anonymous user...');
exec('sudo mysql -u root -e "DELETE FROM mysql.user WHERE User=\'\';"');
system('sudo mysql -u root -e "DELETE FROM mysql.user WHERE User=\'\';"');
// drop test database and anthing familiar
$this->info('Mariadb drop test and anthing familiar...');
exec('sudo mysql -u root -e "DROP DATABASE IF EXISTS test;"');
exec('sudo mysql -u root -e "DELETE FROM mysql.db WHERE Db=\'test\' OR Db=\'test_%\';"');
system('sudo mysql -u root -e "DROP DATABASE IF EXISTS test;"');
system('sudo mysql -u root -e "DELETE FROM mysql.db WHERE Db=\'test\' OR Db=\'test_%\';"');
// remove plugin for root and set password
$this->info('Mariadb add password for root...');
if ($version === '10.4') {
exec('sudo mysql -u root -e "ALTER USER root@localhost IDENTIFIED VIA mysql_native_password USING PASSWORD(\''.$password.'\'); FLUSH PRIVILEGES;"');
system('sudo mysql -u root -e "ALTER USER root@localhost IDENTIFIED VIA mysql_native_password USING PASSWORD(\''.$password.'\'); FLUSH PRIVILEGES;"');
} else {
exec('sudo mysql -u root -e "UPDATE mysql.user SET Password=PASSWORD(\''.$password.'\') WHERE User=\'root\'; FLUSH PRIVILEGES;"');
exec('sudo mysql -u root -e "UPDATE mysql.user SET plugin=\'\' where User=\'root\';"');
system('sudo mysql -u root -e "UPDATE mysql.user SET Password=PASSWORD(\''.$password.'\') WHERE User=\'root\'; FLUSH PRIVILEGES;"');
system('sudo mysql -u root -e "UPDATE mysql.user SET plugin=\'\' where User=\'root\';"');
}
file_put_contents(self::MCP_LOG_FILE, "Mariadb installed\nuser: root\npassword: $password\n--\n", FILE_APPEND);
$this->info('Mariadb installing...Success! \o/ Check '.self::MCP_LOG_FILE);
$this->info('Mariadb installing...Success! \o/');
if ($this->option('remote') === true) {
$this->remoteAccess();
}
} else {
$this->error('Failed! Please check log-file!');
}
}
/**
*
*
*/
private function remoteAccess()
{
$this->info('Mariadb remote...');
system('mkdir -p /etc/mysql/ssl');
system('hostname', $hostname);
$this->info('Generating CA');
system('openssl genrsa 4096 > /etc/mysql/ssl/ca-key.pem');
system('openssl req -new -x509 -nodes -days 365000 -key /etc/mysql/ssl/ca-key.pem -out /etc/mysql/ssl/ca-cert.pem -subj "/CN='.$hostname.'-mysql-ca"');
$this->info('Generating Server Certificate');
system('openssl req -newkey rsa:4096 -days 365000 -nodes -keyout /etc/mysql/ssl/server-key.pem -out /etc/mysql/ssl/server-req.pem -subj "/CN='.$hostname.'-mysql-server"');
system('openssl rsa -in /etc/mysql/ssl/server-key.pem -out /etc/mysql/ssl/server-key.pem');
system('openssl x509 -req -in /etc/mysql/ssl/server-req.pem -days 365000 -CA /etc/mysql/ssl/ca-cert.pem -CAkey /etc/mysql/ssl/ca-key.pem -set_serial 01 -out /etc/mysql/ssl/server-cert.pem');
$this->info('Generating Client Certificate');
system('openssl req -newkey rsa:4096 -days 365000 -nodes -keyout /etc/mysql/ssl/client-key.pem -out /etc/mysql/ssl/client-req.pem -subj "/CN='.$hostname.'-mysql-server"');
system('openssl rsa -in /etc/mysql/ssl/client-key.pem -out /etc/mysql/ssl/client-key.pem');
system('openssl x509 -req -in /etc/mysql/ssl/client-req.pem -days 365000 -CA /etc/mysql/ssl/ca-cert.pem -CAkey /etc/mysql/ssl/ca-key.pem -set_serial 01 -out /etc/mysql/ssl/client-cert.pem');
if (!file_exists('/etc/mysql/ssl/ca-cert.pem') || !file_exists('/etc/mysql/ssl/client-cert.pem') || !file_exists('/etc/mysql/ssl/client-key.pem')) {
$this->error('Failed! Certificates not created!');
exit();
}
$this->info('Validate Certificates');
system('openssl verify -CAfile /etc/mysql/ssl/ca-cert.pem /etc/mysql/ssl/server-cert.pem /etc/mysql/ssl/client-cert.pem');
system('cat >> /etc/mysql/my.cnf << EOF
[mysqld]
bind-address = 0.0.0.0
ssl-ca=/etc/mysql/ssl/ca-cert.pem
ssl-cert=/etc/mysql/ssl/server-cert.pem
ssl-key=/etc/mysql/ssl/server-key.pem
[client]
ssl-ca=/etc/mysql/ssl/ca-cert.pem
ssl-cert=/etc/mysql/ssl/client-cert.pem
ssl-key=/etc/mysql/ssl/client-key.pem');
system('chown -R mysql:mysql /etc/mysql/ssl');
system('chmod 644 /etc/mysql/ssl/*cert*');
system('chmod 644 /etc/mysql/ssl/*key*');
system('systemctl restart mariadb');
system('ufw allow mysql');
$this->info('Mariadb remote...Success! \o/');
}
}

+ 1
- 2
app/Commands/NginxInstallCommand.php View File

@ -77,8 +77,7 @@ class NginxInstallCommand extends Command
$output = [];
$this->info('Nginx adding ufw rules...');
exec('ufw allow "Nginx Full" 2>&1', $output);
$this->line(implode("\n", $output));
system('ufw allow "Nginx Full"');
$this->info('Nginx installing...Success! \o/');
} else {


+ 7
- 7
app/Commands/NodejsInstallCommand.php View File

@ -43,21 +43,21 @@ class NodejsInstallCommand extends Command
$this->info('Nodejs installing...');
exec('curl -sL https://deb.nodesource.com/setup_'.$version.'.x | sudo -E bash -');
exec('apt-get install -y nodejs 2>&1');
system('curl -sL https://deb.nodesource.com/setup_'.$version.'.x | sudo -E bash -');
// @TODO apt add a Warning for no good, in a later version output will be scanned for helpfull infos
exec('apt-get install -y nodejs 2>&1', $output);
$this->line(implode("\n", Install::filterAptMessages($output)));
$this->info('Nodejs add user...');
exec('useradd -d /home/nodejs -m nodejs', $output);
system('useradd -d /home/nodejs -m nodejs');
exec('sudo -Hu nodejs mkdir /home/nodejs/.npm');
exec('sudo -Hu nodejs npm config set prefix /home/nodejs/.npm');
exec('echo -e "export PATH=/home/nodejs/.npm/bin:\$PATH" >> /home/nodejs/.bashrc');
system('sudo -Hu nodejs mkdir /home/nodejs/.npm');
system('sudo -Hu nodejs npm config set prefix /home/nodejs/.npm');
system('echo -e "export PATH=/home/nodejs/.npm/bin:\$PATH" >> /home/nodejs/.bashrc');
$this->info('Nodejs install npm & pm2...');
exec('npm install -g npm && npm install -g pm2');
system('npm install -g npm && npm install -g pm2');
// check if nginx is ready and installed
if (Install::isReady('nodejs')) {


+ 14
- 12
app/Commands/PhpFpmInstallCommand.php View File

@ -46,6 +46,8 @@ class PhpFpmInstallCommand extends Command
{
$this->info('Php-fpm installing...');
$output = [];
exec('apt update 2>&1', $output);
exec('apt install -y php-fpm '.self::PACKAGES.' 2>&1', $output);
@ -64,24 +66,24 @@ class PhpFpmInstallCommand extends Command
$user = $this->option('user');
if ($user) {
$output = [];
$this->info('Php-fpm change user...');
exec('sed -i "s/user = www-data/user = '.$user.'/g" '.$file, $output);
exec('sed -i "s/group = www-data/group = '.$user.'/g" '.$file, $output);
system('sed -i "s/user = www-data/user = '.$user.'/g" '.$file);
system('sed -i "s/group = www-data/group = '.$user.'/g" '.$file);
exec('sed -i "s/listen.owner = www-data/listen.owner = '.$user.'/g" '.$file, $output);
exec('sed -i "s/listen.group = www-data/listen.group = '.$user.'/g" '.$file, $output);
$this->line(implode("\n", $output));
system('sed -i "s/listen.owner = www-data/listen.owner = '.$user.'/g" '.$file);
system('sed -i "s/listen.group = www-data/listen.group = '.$user.'/g" '.$file);
}
$output = [];
$this->info('Php-fpm change mode...');
exec('sed -i "s/;listen.mode = 0660/listen.mode = 0660/g" '.$file, $output);
system('sed -i "s/;listen.mode = 0660/listen.mode = 0660/g" '.$file);
// getting version and restart service
$output = [];
exec("systemctl --full --type service --all | awk '{print $1}' | egrep php.+?fpm.service", $output);
$this->line(implode("\n", $output));
foreach ($output as $line) {
system('service '.str_replace('.service', '', $line).' restart');
}
}
}


+ 28
- 1
app/Factories/NginxVhostFactory.php View File

@ -134,7 +134,34 @@ class NginxVhostFactory
})
->addLineBreak('-')
->addMenuItem($checkbox)
->addLineBreak('-')
->addLineBreak('-');
if ($vhost['ssl'] === true) {
if ($vhost['ssl_certificate_exists']) {
$builder->addStaticItem('expired_at: '.$vhost['ssl_certificate_expired_at']);
} else {
// add certificate
$builder->addItem('add certificate', function(CliMenu $menu) use ($vhost) {
$result = $menu->askText()
->setPromptText('Enter E-Mail')
->ask();
$email = $result->fetch();
system('php '.base_path().'/mcp lets-encrypt:add '.$email.' '.$vhost['domain']);
if ($vhost['redirect_www'] === true) {
system('php '.base_path().'/mcp lets-encrypt:add '.$email.' www.'.$vhost['domain']);
}
});
}
$builder->addLineBreak('-');
}
$builder
->addItem('Back', new NginxVhostGoBackAction())
->addItem('Exit', new ExitAction());


+ 2
- 2
app/Helpers/InstallHelper.php View File

@ -30,7 +30,7 @@ class InstallHelper
{
$result = false;
exec('echo $(dpkg -s nginx 2>&1 | grep "'.self::PACKAGE_STATUS.'")', $output);
exec('echo $(dpkg -s '.$name.' 2>&1 | grep "'.self::PACKAGE_STATUS.'")', $output);
if (isset($output[0]) && $output[0] === self::PACKAGE_STATUS) {
$result = true;
@ -59,7 +59,7 @@ class InstallHelper
}
/**
*
*
*
* @return string
*/


+ 40
- 8
app/Helpers/NginxVhostHelper.php View File

@ -73,24 +73,52 @@ class NginxVhostHelper
$content = file_get_contents($path);
// check for path of sites
preg_match(self::REGEX_SSL_CERTIFICATE, $content, $matches);
preg_match(self::REGEX_SSL_CERTIFICATE_KEY, $content, $matches);
preg_match(self::REGEX_SSL_CERTIFICATE, $content, $certificates);
$matches = array_merge([], $certificates);
preg_match(self::REGEX_SSL_CERTIFICATE_KEY, $content, $certificates);
$matches = array_merge($matches, $certificates);
// check ssl certificates
if (count($matches) >= 2) {
$result['ssl'] = true;
// @TODO find a regex that ignore the ";"
foreach($matches as $index => $match) {
$matches[$index] = str_replace(';', '', $match);
}
if (file_exists($matches[0]) && file_exists($matches[1])) {
$result['ssl_certificate_exists'] = true;
// getting expired
exec('openssl x509 -noout -dates -in '.$matches[0], $openssl);
if (isset($openssl[1])) {
$result['ssl_certificate_expired_at'] = str_replace('notAfter=', '', $openssl[1]);
}
}
}
return $result;
}
// getting expired
exec('openssl x509 -noout -dates -in '.$path, $openssl);
/**
*
* @return
*/
private function getRedirect($path, $domain)
{
// getting .conf-file
$content = file_get_contents($path);
if (isset($openssl[1])) {
$openssl = str_replace('notAfter=', '', $openssl[1]);
// result
$result = false;
}
preg_match('/server_name www.'.$domain.'/', $content, $matches);
if (count($matches) > 0) {
$result = true;
}
return $result;
@ -111,10 +139,14 @@ class NginxVhostHelper
// getting certificates from a configuration
$certificate = $this->getCertificate($path);
// domain
$domain = str_replace('.conf', '', $filename);
$result = array_merge([
'domain' => str_replace('.conf', '', $filename),
'domain' => $domain,
'path' => $path,
'file' => $filename,
'redirect_www' => $this->getRedirect($path, $domain),
'enabled' => in_array($filename, $enabled),
], $certificate);


+ 122
- 0
app/Menus/ItemValidator.php View File

@ -0,0 +1,122 @@
<?php
namespace App\Menus;
use PhpSchool\CliMenu\MenuItem\StaticItem;
use Respect\Validation\Exceptions\NestedValidationException;
/**
*
*
*
*
*/
class ItemValidator
{
// items from menu
private $items;
// current message
private $message;
// validator
private $validator;
/**
*
*
*/
public function __construct($validator)
{
$this->validator = $validator;
$this->message = new StaticItem(''); // must create to compare in items
}
/**
* remove all items from menu and save them
*
* @param [type] $menu
*
*/
private function clear(&$menu)
{
$this->items = $menu->getItems();
foreach($menu->getItems() as $item) {
$menu->removeItem($item);
}
}
/**
* add staticItem after item
*
* @param [type] $menu
* @param [type] $after
* @param [type] $target
*
*/
private function addAfter(&$menu, $after, $target)
{
$this->clear($menu);
foreach($this->items as $item) {
$menu->addItem($item);
if ($after === $item) {
$menu->addItem($target, true);
}
}
}
/**
* remove staticItem
*
*
* @param [type] $menu [description]
* @param [type] $target [description]
*
*/
private function remove(&$menu, $target)
{
$this->clear($menu);
foreach($this->items as $item) {
if ($item === $target) {
continue;
}
$menu->addItem($item);
}
}
/**
* validate,
* if failed show staticItem with errors,
* if not remove staticItem if found
*
* @param [type] $menu
* @param [type] $item
* @param [type] $data
*
*/
public function validate(&$menu, $item, $data)
{
try {
$this->validator->assert($data);
} catch(NestedValidationException $exception) {
$errors = $exception->getMessages();
}
// remove message
$this->remove($menu, $this->message);
// if errors a set add message
if (isset($errors)) {
// @TODO use ColorUtil
$this->message->setText("\033[33m"."\xE2\x9A\xA0 ".join(' ', $errors));
$this->addAfter($menu, $item, $this->message);
}
}
}

+ 32
- 0
app/Menus/Nginx/NginxVhostCancelAction.php View File

@ -0,0 +1,32 @@
<?php
namespace App\Menus\Nginx;
use PhpSchool\CliMenu\CliMenu;
use App\Facades\NginxVhost;
use App\Facades\NginxVhostFactory;
/**
* Action that override default-action for go back
* reload vhosts
*
* @author Björn Hase, Tentakelfabrik
* @license http://opensource.org/licenses/MIT The MIT License
* @link https://gitea.tentakelfabrik.de/Tentakelfabrik/mcp
*
*/
class NginxVhostCancelAction
{
/**
*
* @param CliMenu $menu [description]
* @return [type] [description]
*/
public function __invoke(CliMenu $menu): void
{
$parent = $menu->getParent();
$menu->closeThis();
$parent->open();
}
}

+ 1
- 2
app/Menus/Nginx/NginxVhostGoBackAction.php View File

@ -8,7 +8,7 @@ use App\Facades\NginxVhostFactory;
/**
* Action that override default-action for go back
* reload vhosts
* reload vhosts
*
* @author Björn Hase, Tentakelfabrik
* @license http://opensource.org/licenses/MIT The MIT License
@ -17,7 +17,6 @@ use App\Facades\NginxVhostFactory;
*/
class NginxVhostGoBackAction
{
// index for vhosts
const VHOST_INDEX = 0;
/**


+ 81
- 51
app/Menus/Nginx/TemplateMenuFactory.php View File

@ -7,13 +7,15 @@ use PhpSchool\CliMenu\CliMenu;
use PhpSchool\CliMenu\MenuItem\CheckboxItem;
use PhpSchool\CliMenu\Style\SelectableStyle;
use PhpSchool\CliMenu\MenuItem\MenuMenuItem;
use PhpSchool\CliMenu\MenuItem\StaticItem;
use Respect\Validation\Validator as v;
use Respect\Validation\Exceptions\NestedValidationException;
use App\Menus\ItemValidator;
use App\BladeFile;
use App\Helpers\NginxTemplateHelper;
use App\Facades\TerminalHelper;
/**
@ -29,6 +31,26 @@ class TemplateMenuFactory
// path templates
const TEMPLATES_DIR = '/resources/nginx/templates';
private $configuration = [];
/**
* default configuration
*
* @TODO will be removed after
*
* @return array
*/
private function getConfiguration()
{
return [
'domain' => '',
'root' => '',
'index' => 'index.php',
'ssl' => true,
'redirect_www' => true
];
}
/**
* add item to select template
*
@ -62,24 +84,6 @@ class TemplateMenuFactory
return $menu;
}
/**
* default configuration
*
* @TODO will be removed after
*
* @return array
*/
private function getConfiguration()
{
return [
'domain' => '',
'root' => '',
'index' => 'index.php',
'ssl' => true,
'redirect_www' => true
];
}
/**
* add input item
*
@ -87,20 +91,25 @@ class TemplateMenuFactory
* @param string $label
* @param array $configuration
*/
private function addInputItem($key, $label, &$configuration)
private function addInputItem($key, $label, $itemValidator = NULL)
{
$callable = function(CliMenu $menu) use ($key, $label, &$configuration)
$callable = function(CliMenu $menu) use ($key, $label, $itemValidator)
{
$input = $menu->askText();
if ($configuration[$key]) {
$input->setPlaceholderText($configuration[$key]);
if ($this->configuration[$key]) {
$input->setPlaceholderText($this->configuration[$key]);
}
$result = $input->ask();
$configuration[$key] = $result->fetch();
$this->configuration[$key] = $result->fetch();
$menu->getSelectedItem()->setText($label.': '.$result->fetch());
if ($itemValidator) {
$itemValidator->validate($menu, $menu->getSelectedItem(), [ $key => $result->fetch() ]);
}
$menu->redraw();
};
@ -114,19 +123,16 @@ class TemplateMenuFactory
* @param object $bladeFile
* @param array $configuration
*/
private function addPublishItem($template, $bladeFile, &$configuration)
private function addPublishItem($template, $bladeFile)
{
$callable = function(CliMenu $menu) use ($template, $bladeFile, &$configuration)
$callable = function(CliMenu $menu) use ($template, $bladeFile)
{
// getting configuration
$data = $configuration;
// add directory for validator to check if file exits
$data['index'] = $data['root'].'/'.$data['index'];
$data = $this->configuration;
$validator = v::key('domain', v::domain(false))
->key('root', v::directory())
->key('index', v::file())
->key('root', v::notEmpty())
->key('index', v::notEmpty())
->key('phpFpm', v::notEmpty());
try {
@ -140,12 +146,14 @@ class TemplateMenuFactory
} else {
// create filename
$filename = $configuration['domain'].'.conf';
$filename = $this->configuration['domain'].'.conf';
// write configuration to file
$bladeFile->put($template['name'], '/etc/nginx/sites-available/'.$filename, $configuration);
$bladeFile->put($template['name'], '/etc/nginx/sites-available/'.$filename, $this->configuration);
$menu->confirm('Success!')->display('Ok!');
$this->configuration = $this->getConfiguration();
// invoke action
$action = new NginxVhostGoBackAction();
is_callable($action($menu));
@ -155,6 +163,24 @@ class TemplateMenuFactory
return $callable;
}
/**
*
*
*
*/
private function addCancelItem()
{
$callable = function(CliMenu $menu)
{
$this->configuration = $this->getConfiguration();
$action = new NginxVhostCancelAction();
is_callable($action($menu));
};
return $callable;
}
/**
* adding radio buttons to select php-fpm version
*
@ -162,7 +188,7 @@ class TemplateMenuFactory
* @param CliMenuBuilder $builder
* @param array $configuration
*/
private function addPhpFpmItems($builder, &$configuration)
private function addPhpFpmItems($builder)
{
// get php-fpm services
exec('find /lib/systemd/system/ -name "php[0-9\.]*-fpm.service"', $files);
@ -175,8 +201,8 @@ class TemplateMenuFactory
// remove extension
$file = str_replace('.service', '', $file);
$builder->addRadioItem($file, function(CliMenu $menu) use (&$configuration) {
$configuration['phpFpm'] = $menu->getSelectedItem()->getText();
$builder->addRadioItem($file, function(CliMenu $menu) {
$this->configuration['phpFpm'] = $menu->getSelectedItem()->getText();
});
}
@ -195,38 +221,41 @@ class TemplateMenuFactory
{
$menu = function(CliMenuBuilder $builder) use ($template, $bladeFile)
{
$configuration = $this->getConfiguration();
$this->configuration = $this->getConfiguration();
// create checkbox for ssl
$checkboxSSL = new CheckboxItem('ssl', function(CliMenu $menu) use (&$configuration) {
$configuration['ssl'] = $menu->getSelectedItem()->getChecked();
$checkboxSSL = new CheckboxItem('ssl', function(CliMenu $menu) {
$this->configuration['ssl'] = $menu->getSelectedItem()->getChecked();
});
$checkboxSSL->setChecked($configuration['ssl']);
$checkboxSSL->setChecked($this->configuration['ssl']);
// create checkbox for redirect from www
$checkboxRedirect = new CheckboxItem('redirect www', function(CliMenu $menu) use (&$configuration) {
$configuration['redirect_www'] = $menu->getSelectedItem()->getChecked();
$checkboxRedirect = new CheckboxItem('redirect www', function(CliMenu $menu) {
$this->configuration['redirect_www'] = $menu->getSelectedItem()->getChecked();
});
$checkboxRedirect->setChecked($configuration['redirect_www']);
$checkboxRedirect->setChecked($this->configuration['redirect_www']);
$validator = v::key('root', v::directory());
$itemValidator = new ItemValidator($validator);
$builder
->setTitle('Nginx > Add > '.$template['name'])
->setGoBackButtonText('Cancel')
->disableDefaultItems()
// input domain
->addItem('domain: -', $this->addInputItem('domain', 'domain', $configuration))
->addItem('domain: -', $this->addInputItem('domain', 'domain'))
// input root
->addItem('root: -', $this->addInputItem('root', 'root', $configuration))
->addItem('root: -', $this->addInputItem('root', 'root', $itemValidator))
// input index
->addItem('index: '.$configuration['index'], $this->addInputItem('index', 'index', $configuration))
->addItem('index: '.$this->configuration['index'], $this->addInputItem('index', 'index'))
->addLineBreak('-');
// add php-fpm items
$builder = $this->addPhpFpmItems($builder, $configuration);
$builder = $this->addPhpFpmItems($builder);
$builder
->addLineBreak('-')
@ -237,8 +266,9 @@ class TemplateMenuFactory
->addLineBreak('-')
// create
->addItem('publish', $this->addPublishItem($template, $bladeFile, $configuration))
->addLineBreak('-');
->addItem('Publish', $this->addPublishItem($template, $bladeFile))
->addLineBreak('-')
->addItem('Cancel', $this->addCancelItem());
};
return $menu;


+ 6
- 0
resources/fail2ban/jail.d/mysql-auth.conf View File

@ -0,0 +1,6 @@
[mysqld-auth]
enabled = true
filter = mysqld-auth
port = 3306
logpath = /var/log/mysql/error.log

+ 5
- 0
resources/nginx/templates/empty.blade.php View File

@ -0,0 +1,5 @@
@extends($ssl ? 'layouts.ssl' : 'layouts.no-ssl')
@section('server')
@endsection

+ 1
- 11
resources/nginx/templates/layouts/ssl.blade.php View File

@ -28,19 +28,9 @@ server {
ssl_certificate /etc/letsencrypt/live/{{ $domain }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{ $domain }}/privkey.pem;
include /etc/nginx/snippets/snippets/ssl-params.conf;
include /etc/nginx/snippets/ssl-params.conf;
include /etc/nginx/snippets/secure-headers.conf;
add_header Content-Security-Policy "
default-src 'self';
font-src 'self';
style-src 'self';
img-src 'self';
base-uri 'self';
form-action 'self';
frame-ancestors 'self';
";
@include('partials.default', ['domain' => $domain])
@yield('server')

Loading…
Cancel
Save