ftp user
This commit is contained in:
parent
decf8e09a6
commit
92213c8975
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
config.local.json
|
||||
18
config.dist.json
Normal file
18
config.dist.json
Normal file
@ -0,0 +1,18 @@
|
||||
# www-admtools default configuration
|
||||
# you should override settings in `config.local.json'
|
||||
|
||||
#"dbrootpw": "XXX",
|
||||
#"dbrootpw_file": "/etc/mysql/XXX",
|
||||
"logfile": "/var/log/www-create-site.log",
|
||||
|
||||
"use_owner": 0,
|
||||
"use_site_log": 0,
|
||||
"quota": "off",
|
||||
"use_admin": 0,
|
||||
"use_ftpd": 0,
|
||||
|
||||
"use_ocsp_stapling": 1,
|
||||
"itk_assignuser": 0,
|
||||
"php_fpm": "off",
|
||||
|
||||
# vim: set ft=config tabstop=2 shiftwidth=2 expandtab smarttab:
|
||||
4
crontab.example
Normal file
4
crontab.example
Normal file
@ -0,0 +1,4 @@
|
||||
# www-admtools
|
||||
|
||||
0-59/5 * * * * root /opt/www-admtools/www-update-diskusage
|
||||
|
||||
257
www-create-site
257
www-create-site
@ -1,9 +1,9 @@
|
||||
#!/usr/bin/perl
|
||||
# 2021-08-02 <tom@bitfit.hu> php-fpm + sok egyeb fejlesztes
|
||||
use strict; use warnings; use utf8;
|
||||
use Getopt::Long;
|
||||
use File::Slurp;
|
||||
use Data::Dumper;
|
||||
use JSON;
|
||||
use FindBin;
|
||||
my $self;
|
||||
|
||||
sub sys {
|
||||
@ -74,7 +74,7 @@ sub check_dns {
|
||||
}
|
||||
|
||||
sub generate_random {
|
||||
for my $x (qw(dbpasswd)) {
|
||||
for my $x (qw(dbpasswd ftppasswd adminurl)) {
|
||||
my $passwd = qx(pwgen 12 1);
|
||||
chomp $passwd;
|
||||
if (length($passwd) != 12) {
|
||||
@ -87,6 +87,10 @@ sub generate_random {
|
||||
|
||||
sub filesystem {
|
||||
sys "mkdir $self->{siteroot}";
|
||||
if ($self->{quota} > 0) {
|
||||
my $kb = $self->{quota} * 1024;
|
||||
sys "setquota $self->{user} $kb $kb 0 0 /www";
|
||||
}
|
||||
}
|
||||
|
||||
sub user {
|
||||
@ -94,7 +98,11 @@ sub user {
|
||||
}
|
||||
|
||||
sub siteroot_setup {
|
||||
for my $d (qw(public tmp)) {
|
||||
my @dirs = qw(public tmp);
|
||||
if ($self->{use_site_log}) {
|
||||
push @dirs, 'log';
|
||||
}
|
||||
for my $d (@dirs) {
|
||||
my $path = "$self->{siteroot}/$d";
|
||||
next if -d $path;
|
||||
sys "mkdir $path";
|
||||
@ -110,6 +118,10 @@ sub siteroot_setup {
|
||||
write_file($robotsfn,
|
||||
"User-agent: *\nDisallow: /\n");
|
||||
}
|
||||
if ($self->{use_owner}) {
|
||||
write_file("$self->{siteroot}/.INFO",
|
||||
sprintf("%s\nowner=%s\n", scalar localtime time, $self->{site_owner}));
|
||||
}
|
||||
sys "www-reset-acl $self->{site}";
|
||||
}
|
||||
|
||||
@ -164,55 +176,77 @@ sub letsencrypt {
|
||||
}
|
||||
|
||||
sub apache {
|
||||
my $alias = '';
|
||||
for (my $i = 1; $i < scalar @{$self->{domains}}; $i++) {
|
||||
$alias .= " ServerAlias $self->{domains}->[$i]\n";
|
||||
}
|
||||
chomp $alias;
|
||||
my $itk = '';
|
||||
if ($self->{itk_assignuser}) {
|
||||
$itk = " AssignUserId $self->{user} $self->{group}\n";
|
||||
}
|
||||
my $lt = scalar localtime time;
|
||||
my $cf = <<EOT;
|
||||
# generated: $lt
|
||||
<VirtualHost *:80>
|
||||
ServerName $self->{domains}->[0]
|
||||
$alias
|
||||
ErrorLog \${APACHE_LOG_DIR}/$self->{site}.notls.err.log
|
||||
CustomLog \${APACHE_LOG_DIR}/$self->{site}.notls.log detailed
|
||||
RewriteEngine On
|
||||
RewriteRule ^/(.*) https://%{HTTP_HOST}/\$1 [R,L]
|
||||
</VirtualHost>
|
||||
<VirtualHost *:443>
|
||||
$itk SSLEngine on
|
||||
SSLCertificateFile $self->{tlscrt}
|
||||
SSLCertificateKeyFile $self->{tlskey}
|
||||
SSLUseStapling on
|
||||
SSLStaplingReturnResponderErrors off
|
||||
SSLStaplingFakeTryLater off
|
||||
SSLStaplingStandardCacheTimeout 86400
|
||||
SSLStaplingResponderTimeout 2
|
||||
ServerName $self->{domains}->[0]
|
||||
$alias
|
||||
ErrorLog \${APACHE_LOG_DIR}/$self->{site}.err.log
|
||||
CustomLog \${APACHE_LOG_DIR}/$self->{site}.log detailed
|
||||
DocumentRoot $self->{siteroot}/public
|
||||
<Directory $self->{siteroot}/public>
|
||||
Options -Indexes -FollowSymlinks +SymLinksIfOwnerMatch
|
||||
AllowOverride all
|
||||
Require all granted
|
||||
</Directory>
|
||||
Header always set Strict-Transport-Security "max-age=31536000;"
|
||||
#-[[-www-phpfpm
|
||||
#-]]-www-phpfpm
|
||||
</VirtualHost>
|
||||
# vim: set tabstop=4 shiftwidth=4 expandtab smarttab:
|
||||
EOT
|
||||
write_file($self->{apachecf}, $cf);
|
||||
my $logbase = "\${APACHE_LOG_DIR}/$self->{site}";
|
||||
if ($self->{use_site_log}) {
|
||||
$logbase = "$self->{siteroot}/log/apache";
|
||||
}
|
||||
my $c = "# generated: $lt\n";
|
||||
if ($self->{use_owner}) {
|
||||
$c.="# owner: $self->{site_owner}\n";
|
||||
}
|
||||
$c .= "<VirtualHost *:80>\n".
|
||||
" ServerName $self->{domains}->[0]\n";
|
||||
for (my $i = 1; $i < scalar @{$self->{domains}}; $i++) {
|
||||
$c.=" ServerAlias $self->{domains}->[$i]\n\n";
|
||||
}
|
||||
$c .= " ErrorLog $logbase.notls.err.log\n".
|
||||
" CustomLog $logbase.notls.log detailed\n".
|
||||
" RewriteEngine On\n".
|
||||
" RewriteRule ^/(.*) https://%{HTTP_HOST}/\$1 [R,L]\n".
|
||||
"</VirtualHost>\n".
|
||||
"<VirtualHost *:443>\n";
|
||||
if ($self->{itk_assignuser}) {
|
||||
$c.=" AssignUserId $self->{user} $self->{group}\n\n";
|
||||
}
|
||||
$c .= " SSLEngine on\n".
|
||||
" SSLCertificateFile $self->{tlscrt}\n".
|
||||
" SSLCertificateKeyFile $self->{tlskey}\n";
|
||||
if ($self->{use_ocsp_stapling}) {
|
||||
$c.=" SSLUseStapling on\n".
|
||||
" SSLStaplingReturnResponderErrors off\n".
|
||||
" SSLStaplingFakeTryLater off\n".
|
||||
" SSLStaplingStandardCacheTimeout 86400\n".
|
||||
" SSLStaplingResponderTimeout 2\n";
|
||||
}
|
||||
$c .= " ServerName $self->{domains}->[0]\n";
|
||||
for (my $i = 1; $i < scalar @{$self->{domains}}; $i++) {
|
||||
$c.=" ServerAlias $self->{domains}->[$i]\n\n";
|
||||
}
|
||||
$c .= " ErrorLog $logbase.err.log\n".
|
||||
" CustomLog $logbase.log detailed\n".
|
||||
" DocumentRoot $self->{siteroot}/public\n".
|
||||
" <Directory $self->{siteroot}/public>\n".
|
||||
" Options -Indexes -FollowSymlinks +SymLinksIfOwnerMatch\n".
|
||||
" AllowOverride all\n".
|
||||
" Require all granted\n".
|
||||
" </Directory>\n".
|
||||
" Header always set Strict-Transport-Security \"max-age=31536000;\"\n";
|
||||
if ($self->{use_admin}) {
|
||||
$c.=" Alias /.$self->{adminurl}/ /var/www/_admin/\n";
|
||||
}
|
||||
if ($self->{php_fpm} and $self->{php_fpm} =~ /^\d/) {
|
||||
$c.=" #-[[-www-phpfpm\n".
|
||||
" #-]]-www-phpfpm\n";
|
||||
}
|
||||
$c .= "</VirtualHost>\n".
|
||||
"# vim: set tabstop=4 shiftwidth=4 expandtab smarttab:\n";
|
||||
write_file($self->{apachecf}, $c);
|
||||
sys "apachectl graceful";
|
||||
}
|
||||
|
||||
sub proftpd {
|
||||
return if !$self->{use_ftpd};
|
||||
my $uid = qx(id -u $self->{user});
|
||||
chomp $uid;
|
||||
if ($uid < 10000 or $uid > 11000) {
|
||||
die "got invalid uid for user '$self->{user}': $uid\n";
|
||||
}
|
||||
my $gid = qx(id -g $self->{user});
|
||||
chomp $gid;
|
||||
sys "echo $self->{ftppasswd} | ftpasswd --stdin --passwd --file /etc/proftpd/ftpd.passwd --name $self->{ftpuser} --home $self->{siteroot} --shell /bin/false --uid $uid --gid $gid";
|
||||
}
|
||||
|
||||
sub php_fpm {
|
||||
if ($self->{php_fpm} and $self->{php_fpm} =~ /^\d/) {
|
||||
sys "www-phpfpm -s $self->{site} -p $self->{php_fpm}";
|
||||
@ -229,44 +263,104 @@ sub find_defcert {
|
||||
return qx{hostname -f};
|
||||
}
|
||||
|
||||
sub read_config {
|
||||
my ($fn) = @_;
|
||||
my $contents;
|
||||
eval {
|
||||
$contents = read_file($fn);
|
||||
};
|
||||
return if $@;
|
||||
my $cf;
|
||||
eval {
|
||||
my $json = JSON->new->utf8->relaxed;
|
||||
$cf = $json->decode("{ $contents }");
|
||||
};
|
||||
if ($@) {
|
||||
print STDERR "warning: configuration $fn has JSON errors: $@\n";
|
||||
return;
|
||||
}
|
||||
for my $k (keys %$cf) {
|
||||
$self->{$k} = $cf->{$k};
|
||||
}
|
||||
}
|
||||
|
||||
$self = {
|
||||
site => "",
|
||||
config_files => [
|
||||
"$FindBin::RealBin/config.dist.json",
|
||||
"$FindBin::RealBin/config.local.json"],
|
||||
use_site_log => 0,
|
||||
use_owner => 0,
|
||||
use_admin => 0,
|
||||
use_ftpd => 0,
|
||||
site => '',
|
||||
domains => [],
|
||||
dbpasswd => "",
|
||||
dbrootpw => read_file('/etc/mysql/jelszo') =~ s/\s//gr,
|
||||
dbpasswd => '',
|
||||
dbrootpw_file => undef,
|
||||
dbrootpw => undef,
|
||||
letsencrypt_dom => "/etc/dehydrated/domains.txt",
|
||||
resolver => '8.8.8.8',
|
||||
defcert => 0,
|
||||
defcert_name => find_defcert,
|
||||
no_dnscheck => 0,
|
||||
itk_assignuser => 0,
|
||||
php_fpm => '7.4',
|
||||
use_ocsp_stapling => 1,
|
||||
php_fpm => 'off',
|
||||
quota => 'off',
|
||||
logfile => '/var/log/www-create-site.log',
|
||||
};
|
||||
my $usage = <<EOT;
|
||||
usage: $0 OPTIONS..
|
||||
-s|--site NAME new website's short name ([a-z][a-z0-9]*)
|
||||
-d|--domain 'D [D..]' list of domain names for website
|
||||
--mysqlrootpw PASSWD mysql root password (default is set)
|
||||
--defcert use server's own certificate (skip letsencrypt)
|
||||
--no-dns-check skip dns check (with implicit --defcert)
|
||||
--itk-assignuser use mpm-itk AssignUserId (default: $self->{itk_assignuser})
|
||||
--php-fpm VER create PHP FPM configuration (default VER: $self->{php_fpm})
|
||||
EOT
|
||||
|
||||
sub usage {
|
||||
my $t = "usage: $0 OPTIONS..\n";
|
||||
if ($self->{use_owner}) {
|
||||
$t.=" -o|--owner NAME new website's owner (mandatory)\n";
|
||||
}
|
||||
$t .= " -s|--site NAME new website's short name ([a-z][a-z0-9]*)\n".
|
||||
" -d|--domain 'D [D..]' list of domain names for website\n".
|
||||
" --mysqlrootpw PASSWD mysql root password (default: read from file)\n".
|
||||
" --defcert skip letsencrypt, use server's cert: $self->{defcert_name}\n".
|
||||
" --no-dns-check skip dns check (with implicit --defcert)\n".
|
||||
" --[no]ocsp-stapling set SSLUseStapling on (default: ".
|
||||
($self->{use_ocsp_stapling} ? 'yes' : 'no') .")\n".
|
||||
" --[no]itk-assignuser use mpm-itk AssignUserId (default: ".
|
||||
($self->{itk_assignuser} ? 'yes' : 'no') .")\n".
|
||||
" --php-fpm {off|VER} create PHP FPM configuration (default: $self->{php_fpm})\n".
|
||||
" --[no]adminurl create site specific admin URL (default: ".
|
||||
($self->{use_admin} ? 'yes' : 'no') .")\n".
|
||||
" --[no]ftpd create proftpd user (default: ".
|
||||
($self->{use_ftpd} ? 'yes' : 'no') .")\n".
|
||||
" --quota {off|SIZE} quota size in MB (default: $self->{quota})\n";
|
||||
return $t;
|
||||
}
|
||||
|
||||
for my $cfn (@{$self->{config_files}}) {
|
||||
read_config($cfn);
|
||||
}
|
||||
|
||||
GetOptions(
|
||||
"owner|o=s" => \$self->{site_owner},
|
||||
"site|s=s" => \$self->{site},
|
||||
"domain|d=s@" => \$self->{domains},
|
||||
"mysqlrootpw=s" => \$self->{dbrootpw},
|
||||
"defcert" => \$self->{defcert},
|
||||
"no-dns-check" => \$self->{no_dnscheck},
|
||||
"itk-assignuser" => \$self->{itk_assignuser},
|
||||
"no-itk-assignuser" => sub { $self->{itk_assignuser} = 0; },
|
||||
"ocsp-stapling" => \$self->{use_ocsp_stapling},
|
||||
"no-ocsp-stapling" => sub { $self->{use_ocsp_stapling} = 0; },
|
||||
"php-fpm=s" => \$self->{php_fpm},
|
||||
'help|h' => sub { print $usage; exit 0 }
|
||||
) or die $usage;
|
||||
"adminurl" => \$self->{use_admin},
|
||||
"noadminurl" => sub { $self->{use_admin} = 0; },
|
||||
"ftpd" => \$self->{use_ftpd},
|
||||
"noftpd" => sub { $self->{use_ftpd} = 0; },
|
||||
"quota=s" => \$self->{quota},
|
||||
'help|h' => sub { print usage(); exit 0 }
|
||||
) or die usage();
|
||||
|
||||
if (!$self->{site}) {
|
||||
die "no site name given\n$usage";
|
||||
die "no site name given\n". usage();
|
||||
}
|
||||
if ($self->{use_owner} and !$self->{site_owner}) {
|
||||
die "no site owner given\n";
|
||||
}
|
||||
if ($self->{site} !~ /^[a-z][a-z0-9]*$/) {
|
||||
die "site contains invalid characters: $self->{site}\n";
|
||||
@ -284,6 +378,7 @@ $self->{user} = "www-$self->{site}";
|
||||
$self->{group} = $self->{user};
|
||||
$self->{dbname} = $self->{site};
|
||||
$self->{dbuser} = $self->{site};
|
||||
$self->{ftpuser} = $self->{site};
|
||||
#
|
||||
$self->{tlscrt} = "/etc/dehydrated/certs/$self->{domains}->[0]/fullchain.pem";
|
||||
$self->{tlskey} = "/etc/dehydrated/certs/$self->{domains}->[0]/privkey.pem";
|
||||
@ -293,6 +388,12 @@ if ($self->{defcert}) {
|
||||
$self->{tlskey} = "/etc/dehydrated/certs/$self->{defcert_name}/privkey.pem";
|
||||
}
|
||||
|
||||
if (!defined $self->{dbrootpw}) {
|
||||
if (!defined $self->{dbrootpw_file}) {
|
||||
die "no mysql root password, or password file given\n";
|
||||
}
|
||||
$self->{dbrootpw} = read_file($self->{dbrootpw_file}) =~ s/\s//gr;
|
||||
}
|
||||
|
||||
check_exists;
|
||||
if (!$self->{no_dnscheck}) {
|
||||
@ -308,16 +409,26 @@ if (!$self->{defcert} and !$self->{no_dnscheck}) {
|
||||
letsencrypt;
|
||||
}
|
||||
apache;
|
||||
proftpd;
|
||||
php_fpm;
|
||||
|
||||
my $domains = join(' ', @{$self->{domains}});
|
||||
my $sitedata = <<EOT;
|
||||
Siteroot: $self->{siteroot}
|
||||
Domain: $domains
|
||||
MySQL db: $self->{dbname}
|
||||
MySQL user: $self->{dbuser}
|
||||
MySQL passwd: $self->{dbpasswd}
|
||||
EOT
|
||||
my $sitedata =
|
||||
"Siteroot: $self->{siteroot}\n".
|
||||
"Domain: $domains\n".
|
||||
"MySQL db: $self->{dbname}\n".
|
||||
"MySQL user: $self->{dbuser}\n".
|
||||
"MySQL passwd: $self->{dbpasswd}\n";
|
||||
if ($self->{use_admin}) {
|
||||
$sitedata .=
|
||||
"MySQL Adminer: https://$self->{domains}->[0]/.$self->{adminurl}/adminer.php\n";
|
||||
}
|
||||
if ($self->{use_ftpd}) {
|
||||
$sitedata .=
|
||||
"FTP: $self->{domains}->[0] (port 21, plain + TLS)\n".
|
||||
"FTP user: $self->{ftpuser}\n".
|
||||
"FTP passwd: $self->{ftppasswd}\n";
|
||||
}
|
||||
|
||||
print "\n$sitedata";
|
||||
umask(0066);
|
||||
|
||||
@ -9,9 +9,10 @@ rm /etc/apache2/sites-enabled/$site.conf
|
||||
apachectl graceful
|
||||
www-phpfpm -s $site -d
|
||||
deluser www-$site
|
||||
sed -i -e '/^$site:/d' /etc/proftpd/ftpd.passwd
|
||||
echo 'DROP DATABASE $site;' | mysql -u root -p`cat /etc/mysql/jelszo`
|
||||
echo 'DROP USER $site\@localhost;' | mysql -u root -p`cat /etc/mysql/jelszo`
|
||||
rm -r /www/$site
|
||||
- letsencrypt domains.txt-bol kivenni, revoke certificate
|
||||
-> remove from letsencrypt domains.txt, revoke certificate
|
||||
EOT
|
||||
# vim: set tabstop=4 shiftwidth=4 expandtab smarttab:
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
#!/usr/bin/perl
|
||||
# 2021-08-02 <tom@bitfit.hu>
|
||||
use strict; use warnings; use utf8;
|
||||
use Getopt::Long;
|
||||
use File::Slurp;
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
#!/bin/bash
|
||||
# 2021-08-02 <tom@bitfit.hu> www-data r-x
|
||||
d=$1
|
||||
if [ -z "$d" ]; then
|
||||
echo "usage: $0 <directory under /www>"
|
||||
|
||||
21
www-update-diskusage
Executable file
21
www-update-diskusage
Executable file
@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
if ! mountpoint -q /www; then
|
||||
echo "/www not a mountpoint" >&2
|
||||
exit 1
|
||||
fi
|
||||
cd /www
|
||||
|
||||
for d in *; do
|
||||
if [[ $d == lost+found ]]; then continue; fi
|
||||
if [[ $d == aquota.user ]]; then continue; fi
|
||||
if [[ $d == _admin ]]; then continue; fi
|
||||
if mountpoint -q $d; then
|
||||
(date; df -mP $d) > $d/disk_usage.txt
|
||||
else
|
||||
Q=$(quota -u www-$d 2>/dev/null)
|
||||
if [[ $? == 0 ]]; then
|
||||
(date; echo "$Q") > $d/disk_usage.txt
|
||||
fi
|
||||
fi
|
||||
done
|
||||
# vim: set tabstop=4 shiftwidth=4 expandtab smarttab:
|
||||
Loading…
x
Reference in New Issue
Block a user