forked from zer.ooo/web
[wip]
This commit is contained in:
parent
23c3cf530e
commit
ebddf2f031
35 changed files with 693 additions and 155 deletions
6
Vagrantfile
vendored
6
Vagrantfile
vendored
|
@ -8,7 +8,7 @@ Vagrant.configure(2) do |config|
|
||||||
web.vm.provision :shell, inline: <<installphp
|
web.vm.provision :shell, inline: <<installphp
|
||||||
add-apt-repository -y ppa:ondrej/php >/dev/null
|
add-apt-repository -y ppa:ondrej/php >/dev/null
|
||||||
apt-get -qq update 2>/dev/null
|
apt-get -qq update 2>/dev/null
|
||||||
apt-get -qq install php7.0-cli php7.0-sqlite3 2>/dev/null >/dev/null;
|
apt-get -qq install php7.0-cli php7.0-sqlite3 php7.0-zip 2>/dev/null >/dev/null;
|
||||||
start-stop-daemon -bS --quiet --make-pidfile --pidfile /var/run/zerooo.pid --startas /bin/bash -- -c "exec php -S 0:8888 -t /vagrant/public/ /vagrant/public/index.php > /var/log/zerooo.log 2>&1";
|
start-stop-daemon -bS --quiet --make-pidfile --pidfile /var/run/zerooo.pid --startas /bin/bash -- -c "exec php -S 0:8888 -t /vagrant/public/ /vagrant/public/index.php > /var/log/zerooo.log 2>&1";
|
||||||
installphp
|
installphp
|
||||||
web.vm.network "forwarded_port", guest: 8888, host: 8888
|
web.vm.network "forwarded_port", guest: 8888, host: 8888
|
||||||
|
@ -18,6 +18,10 @@ installphp
|
||||||
vpn.vm.box = "ubuntu/wily64"
|
vpn.vm.box = "ubuntu/wily64"
|
||||||
vpn.vm.network "private_network", ip: "192.168.50.8"
|
vpn.vm.network "private_network", ip: "192.168.50.8"
|
||||||
vpn.vm.synced_folder "../zer.ooo-server", "/server"
|
vpn.vm.synced_folder "../zer.ooo-server", "/server"
|
||||||
|
vpn.vm.network "forwarded_port", guest: 7864, host: 7864
|
||||||
|
vpn.vm.provision :shell, inline: <<installnode
|
||||||
|
bash /server/main.sh all "http://192.168.50.4:8888";
|
||||||
|
installnode
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
BINDIR=$(dirname $(realpath $0));
|
|
||||||
BASEDIR=$(realpath "$BINDIR/../");
|
set -e;
|
||||||
cd $BASEDIR;
|
|
||||||
|
BINDIR="$(dirname $(realpath "${0}"))";
|
||||||
|
BASEDIR="$(realpath "${BINDIR}/../")";
|
||||||
|
cd "${BASEDIR}";
|
||||||
rm -rf ./storage/ca/*;
|
rm -rf ./storage/ca/*;
|
||||||
mkdir ./storage/ca/certs;
|
mkdir ./storage/ca/certs;
|
||||||
touch ./storage/ca/{,certs/}.gitkeep;
|
touch ./storage/ca/certs/.gitkeep ./storage/ca/.gitkeep;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
BASEDIR=$(realpath $(dirname $0));
|
BASEDIR=$(realpath $(dirname $0));
|
||||||
KEYDIR=$(realpath "$BASEDIR/../storage/ca/");
|
KEYDIR=$(realpath "$BASEDIR/../storage/ca/");
|
||||||
|
|
||||||
if [ -f $KEYDIR/ca.key ]; then
|
if [ -f "$KEYDIR/ca.key" ]; then
|
||||||
echo "CA key already exists. not overwriting it."
|
echo "CA key already exists. not overwriting it."
|
||||||
exit 1;
|
exit 1;
|
||||||
fi
|
fi
|
||||||
|
@ -10,7 +10,7 @@ fi
|
||||||
CN="ob.ae-cn";
|
CN="ob.ae-cn";
|
||||||
|
|
||||||
if [ ! -z "$1" ]; then
|
if [ ! -z "$1" ]; then
|
||||||
CN=$1;
|
CN="$1";
|
||||||
fi;
|
fi;
|
||||||
|
|
||||||
openssl req -days 3650 -nodes -new -x509 -keyout $KEYDIR/ca.key -out $KEYDIR/ca.crt -subj "/CN=$CN" -extensions ca_ext -config "$BASEDIR/../etc/openssl.conf";
|
openssl req -days 3650 -nodes -new -x509 -keyout "$KEYDIR/ca.key" -out "$KEYDIR/ca.crt" -subj "/CN=$CN" -extensions ca_ext -config "$BASEDIR/../etc/openssl.conf";
|
||||||
|
|
22
bin/setup
22
bin/setup
|
@ -1,15 +1,17 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e;
|
||||||
|
|
||||||
BASEDIR="$(dirname $(realpath $0))/../";
|
BASEDIR="$(dirname $(realpath $0))/../";
|
||||||
cd $BASEDIR;
|
cd "${BASEDIR}";
|
||||||
STORAGE=$(realpath "$BASEDIR/storage/ca");
|
STORAGE="$(realpath "${BASEDIR}/storage/ca")";
|
||||||
|
|
||||||
mkdir -p $STORAGE;
|
mkdir -p "${STORAGE}/certs";
|
||||||
mkdir -p $STORAGE/certs;
|
|
||||||
|
|
||||||
echo 01 > $STORAGE/serial;
|
echo 01 > "${STORAGE}/serial";
|
||||||
echo 01 > $STORAGE/crl_serial;
|
echo 01 > "${STORAGE}/crl_serial";
|
||||||
touch $STORAGE/database;
|
touch "${STORAGE}/database";
|
||||||
touch $STORAGE/database.attr;
|
touch "${STORAGE}/database.attr";
|
||||||
|
|
||||||
$BASEDIR/bin/create-ca $1;
|
"${BASEDIR}/bin/create-ca" $1;
|
||||||
$BASEDIR/bin/create-crl;
|
"${BASEDIR}/bin/create-crl";
|
|
@ -1,6 +1,9 @@
|
||||||
prefix: Eater\Glim\Handler\
|
prefix: Eater\Glim\Handler\
|
||||||
routes:
|
routes:
|
||||||
/: Home
|
/: Home
|
||||||
|
/install:
|
||||||
|
get: Install\Show
|
||||||
|
post: Install\Action
|
||||||
/login:
|
/login:
|
||||||
get: Login\Show
|
get: Login\Show
|
||||||
post: Login\Action
|
post: Login\Action
|
||||||
|
@ -19,11 +22,15 @@ routes:
|
||||||
/revoke:
|
/revoke:
|
||||||
post: Panel\Certificates\Revoke
|
post: Panel\Certificates\Revoke
|
||||||
/server:
|
/server:
|
||||||
|
/remove:
|
||||||
|
post: Panel\Servers\Remove
|
||||||
/sign:
|
/sign:
|
||||||
post: Panel\Servers\Sign
|
post: Panel\Servers\Sign
|
||||||
/{fingerprint}:
|
/{fingerprint}:
|
||||||
get: Panel\Servers\Edit\Show
|
get: Panel\Servers\Edit\Show
|
||||||
post: Panel\Servers\Edit\Action
|
post: Panel\Servers\Edit\Action
|
||||||
|
/{fingerprint}/config: Panel\Servers\Config
|
||||||
|
/{fingerprint}/config/{cert}: Panel\Servers\Config
|
||||||
/server:
|
/server:
|
||||||
/register:
|
/register:
|
||||||
post: Server\Register
|
post: Server\Register
|
|
@ -31,7 +31,7 @@ subjectKeyIdentifier=hash
|
||||||
authorityKeyIdentifier = keyid,issuer:always
|
authorityKeyIdentifier = keyid,issuer:always
|
||||||
extendedKeyUsage = serverAuth
|
extendedKeyUsage = serverAuth
|
||||||
keyUsage = digitalSignature,keyEncipherment
|
keyUsage = digitalSignature,keyEncipherment
|
||||||
crlDistributionPoints = URI:http://0b.ae/crl
|
crlDistributionPoints = URI:http://localhost:8888/crl
|
||||||
[client_ext]
|
[client_ext]
|
||||||
subjectKeyIdentifier=hash
|
subjectKeyIdentifier=hash
|
||||||
basicConstraints = CA:FALSE
|
basicConstraints = CA:FALSE
|
||||||
|
|
|
@ -14,15 +14,17 @@ $(function () {
|
||||||
function save(callback) {
|
function save(callback) {
|
||||||
var fingerprint = $('.server-form').data('fingerprint');
|
var fingerprint = $('.server-form').data('fingerprint');
|
||||||
|
|
||||||
var data = $('.server-form input').serializeArray();
|
var data = $('.server-form input,.server-form select').serializeArray();
|
||||||
|
|
||||||
$.post('/panel/server/' + fingerprint, data, function (data) {
|
$.post('/panel/server/' + fingerprint, data, function (data) {
|
||||||
|
console.log(data);
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
$('h2').after(error.text(data.error));
|
$('h2').after(error.text(data.error));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
}, 'json');
|
||||||
}
|
}
|
||||||
|
|
||||||
function sign() {
|
function sign() {
|
||||||
|
@ -35,8 +37,12 @@ $(function () {
|
||||||
function (data) {
|
function (data) {
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
$('h2').after(error.text(data.error));
|
$('h2').after(error.text(data.error));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
location.reload();
|
||||||
|
},
|
||||||
|
'json'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
|
@ -9,8 +9,8 @@ $(function () {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!/^[A-Za-z0-9_-]+$/.test(name)) {
|
if (!/^[A-Za-z0-9\-]+$/.test(name)) {
|
||||||
$('h2').after(error.text("Only alphanumeric, _ and - allowed in name"));
|
$('h2').after(error.text("Only alphanumeric and - allowed in name"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ $(function () {
|
||||||
} else {
|
} else {
|
||||||
$('h2').after(error.text(data.error));
|
$('h2').after(error.text(data.error));
|
||||||
}
|
}
|
||||||
});
|
}, 'json');
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#wantsPassword").change(function () {
|
$("#wantsPassword").change(function () {
|
||||||
|
|
|
@ -26,4 +26,11 @@ $(function(){
|
||||||
|
|
||||||
$('.revoke-password').val('');
|
$('.revoke-password').val('');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('.remove').click(function () {
|
||||||
|
var that = this;
|
||||||
|
$.post('/panel/server/remove',{ fingerprint: $(this).data('fingerprint') }, function () {
|
||||||
|
$(that).parents('tr').first().remove();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
|
@ -324,6 +324,7 @@ class Core implements ContainerInterface
|
||||||
public function handle($class, Request $request, Response $response, ContainerInterface $containerInterface)
|
public function handle($class, Request $request, Response $response, ContainerInterface $containerInterface)
|
||||||
{
|
{
|
||||||
$this->startTimer(['response']);
|
$this->startTimer(['response']);
|
||||||
|
/** @var Handler\Main $handler */
|
||||||
$handler = new $class($this, $request, $response, $containerInterface);
|
$handler = new $class($this, $request, $response, $containerInterface);
|
||||||
|
|
||||||
$this->startTimer(['response/before-handle']);
|
$this->startTimer(['response/before-handle']);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace Eater\Glim\Handler;
|
namespace Eater\Glim\Handler;
|
||||||
|
|
||||||
class CA extends Session
|
class CA extends Main
|
||||||
{
|
{
|
||||||
function handle()
|
function handle()
|
||||||
{
|
{
|
||||||
|
|
21
src/Handler/CRL.php
Normal file
21
src/Handler/CRL.php
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Created by PhpStorm.
|
||||||
|
* User: eater
|
||||||
|
* Date: 6/6/16
|
||||||
|
* Time: 11:28 PM
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Eater\Glim\Handler;
|
||||||
|
|
||||||
|
|
||||||
|
class CRL extends Main
|
||||||
|
{
|
||||||
|
function handle()
|
||||||
|
{
|
||||||
|
return $this->getResponse()
|
||||||
|
->withHeader('Content-Type', 'plain/text')
|
||||||
|
->withHeader('Content-Disposition', 'attachment; filename="crl.pem"')
|
||||||
|
->write(file_get_contents($this->getCore()->getBaseDir() . '/storage/ca/crl.pem'));
|
||||||
|
}
|
||||||
|
}
|
53
src/Handler/Install/Action.php
Normal file
53
src/Handler/Install/Action.php
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Eater\Glim\Handler\Install;
|
||||||
|
|
||||||
|
use Eater\Glim\Service\User;
|
||||||
|
|
||||||
|
class Action extends Show
|
||||||
|
{
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
/** @var User $user */
|
||||||
|
$user = $this->get('user');
|
||||||
|
|
||||||
|
$caCN = $this->post('ca-cn');
|
||||||
|
|
||||||
|
/* @var \Aura\Session\Session $session */
|
||||||
|
$session = $this->get('session');
|
||||||
|
$segment = $session->getSegment('main');
|
||||||
|
|
||||||
|
if (trim($caCN) === "") {
|
||||||
|
$segment->setFlash('error', 'CA Common name can\'t be empty');
|
||||||
|
return $this->redirect('/install');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var \Twig_Environment $twig */
|
||||||
|
$twig = $this->get('twig');
|
||||||
|
|
||||||
|
$opensslConf = $twig->render('etc/openssl-ca.conf.twig', [
|
||||||
|
'host' => $this->post('domainWithPort')
|
||||||
|
]);
|
||||||
|
|
||||||
|
file_put_contents($this->getCore()->getBaseDir() . '/etc/openssl.conf', $opensslConf);
|
||||||
|
|
||||||
|
exec($this->getCore()->getBaseDir() . '/bin/clean-all');
|
||||||
|
exec($this->getCore()->getBaseDir() . '/bin/setup ' . escapeshellarg($caCN));
|
||||||
|
|
||||||
|
$newUser = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$newUser = $user->createSuperuser($this->post('username'), $this->post('password'));
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$segment->setFlash("error", $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($newUser === null) {
|
||||||
|
return $this->redirect('/install');
|
||||||
|
}
|
||||||
|
|
||||||
|
$segment->set('user', $newUser);
|
||||||
|
|
||||||
|
return $this->redirect('/panel');
|
||||||
|
}
|
||||||
|
}
|
54
src/Handler/Install/Show.php
Normal file
54
src/Handler/Install/Show.php
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Eater\Glim\Handler\Install;
|
||||||
|
|
||||||
|
use Eater\Glim\Handler\Main;
|
||||||
|
use Eater\Glim\Model\UserQuery;
|
||||||
|
|
||||||
|
class Show extends Main
|
||||||
|
{
|
||||||
|
public function beforeHandle() {
|
||||||
|
if (UserQuery::create()->findOne()) {
|
||||||
|
return $this->redirect('/login');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$return = 1;
|
||||||
|
$execEnabled = $this->isExecEnabled();
|
||||||
|
$hasOpenSsl = false;
|
||||||
|
if ($execEnabled) {
|
||||||
|
exec('command -v openssl', $output, $return);
|
||||||
|
$hasOpenSsl = $return === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'hasExecEnabled' => $execEnabled,
|
||||||
|
'hasOpenSsl' => $hasOpenSsl,
|
||||||
|
'hasOpenSslExtension' => extension_loaded('openssl'),
|
||||||
|
'hasZipExtension' => extension_loaded('zip'),
|
||||||
|
'hostname' => parse_url($_SERVER['HTTP_HOST'], PHP_URL_HOST),
|
||||||
|
'hostnameWithPort' => $_SERVER['HTTP_HOST']
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->render('install.html.twig', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isExecEnabled() {
|
||||||
|
if (ini_get('safe_mode')) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
$d = ini_get('disable_functions');
|
||||||
|
$s = ini_get('suhosin.executor.func.blacklist');
|
||||||
|
if ("$d$s") {
|
||||||
|
$array = preg_split('/,\s*/', "$d,$s");
|
||||||
|
if (in_array('exec', $array)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,9 +11,25 @@ namespace Eater\Glim\Handler\Login;
|
||||||
|
|
||||||
use Aura\Session\Segment;
|
use Aura\Session\Segment;
|
||||||
use Eater\Glim\Handler\Main;
|
use Eater\Glim\Handler\Main;
|
||||||
|
use Eater\Glim\Model\UserQuery;
|
||||||
|
|
||||||
class Show extends Main
|
class Show extends Main
|
||||||
{
|
{
|
||||||
|
public function beforeHandle()
|
||||||
|
{
|
||||||
|
$session = $this->get('session');
|
||||||
|
$segment = $session->getSegment('main');
|
||||||
|
$user = $segment->get('user');
|
||||||
|
|
||||||
|
if ($user) {
|
||||||
|
$this->redirect('/panel');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!UserQuery::create()->findOne()) {
|
||||||
|
return $this->redirect('/install');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
/** @var Segment $segment */
|
/** @var Segment $segment */
|
||||||
|
|
|
@ -146,7 +146,6 @@ abstract class Main implements ContainerInterface
|
||||||
return $this->getResponse()
|
return $this->getResponse()
|
||||||
->withHeader('Content-Type', 'application/json')
|
->withHeader('Content-Type', 'application/json')
|
||||||
->write(json_encode($array));
|
->write(json_encode($array));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -29,7 +29,7 @@ class Download extends Session
|
||||||
|
|
||||||
return $this->getResponse()
|
return $this->getResponse()
|
||||||
->withHeader('Content-Type', 'plain/text')
|
->withHeader('Content-Type', 'plain/text')
|
||||||
->withHeader('Content-Disposition', 'attachment; filename="' . $name . '.' . $this->getUser()->getUsername() .'.crt"')
|
->withHeader('Content-Disposition', 'attachment; filename="' . $name . '.' . $cert->getSerial() .'.crt"')
|
||||||
->write($cert->getCertificate());
|
->write($cert->getCertificate());
|
||||||
}
|
}
|
||||||
}
|
}
|
89
src/Handler/Panel/Servers/Config.php
Normal file
89
src/Handler/Panel/Servers/Config.php
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Created by PhpStorm.
|
||||||
|
* User: eater
|
||||||
|
* Date: 6/12/16
|
||||||
|
* Time: 3:26 PM
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Eater\Glim\Handler\Panel\Servers;
|
||||||
|
|
||||||
|
|
||||||
|
use Eater\Glim\Handler\Session;
|
||||||
|
use Eater\Glim\Model\Certificate;
|
||||||
|
use Eater\Glim\Model\CertificateQuery;
|
||||||
|
use Eater\Glim\Model\Server;
|
||||||
|
use Eater\Glim\Model\ServerQuery;
|
||||||
|
|
||||||
|
class Config extends Session
|
||||||
|
{
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$zipFile = tempnam(sys_get_temp_dir(), '0zip');
|
||||||
|
$zip = new \ZipArchive();
|
||||||
|
$zip->open($zipFile, \ZipArchive::CREATE);
|
||||||
|
$server = ServerQuery::create()->findOneByFingerprint($this->attr('fingerprint'));
|
||||||
|
$name = $server->getFqdn();
|
||||||
|
|
||||||
|
$this->fillZipWithCaAndConfig($zip, $server);
|
||||||
|
|
||||||
|
$cert = $this->attr('cert');
|
||||||
|
|
||||||
|
if ($cert !== null) {
|
||||||
|
$certModel = CertificateQuery::create()->findOneByUserAndName($this->getUser(), $cert);
|
||||||
|
$this->addClientCertificateData($zip, $certModel);
|
||||||
|
|
||||||
|
$name .= '-' . $certModel->getName() . '.' . $certModel->getSerial();
|
||||||
|
}
|
||||||
|
|
||||||
|
$zip->close();
|
||||||
|
|
||||||
|
$zipContents = file_get_contents($zipFile);
|
||||||
|
unlink($zipFile);
|
||||||
|
|
||||||
|
return $this->getResponse()
|
||||||
|
->withHeader('Content-Type', 'application/zip')
|
||||||
|
->withHeader('Content-Disposition', 'attachment; filename="' . $name . '.zip"')
|
||||||
|
->write($zipContents);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \ZipArchive $zip
|
||||||
|
* @param Server $server
|
||||||
|
*/
|
||||||
|
public function fillZipWithCaAndConfig($zip, $server)
|
||||||
|
{
|
||||||
|
$config = $this->getConfigForServerFingerprint($server);
|
||||||
|
$zip->addFromString('server.conf', $config);
|
||||||
|
$zip->addFromString('ca.crt', file_get_contents($this->getCore()->getBaseDir() . '/storage/ca/ca.crt'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Server $server
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getConfigForServerFingerprint($server)
|
||||||
|
{
|
||||||
|
/** @var \Twig_Environment $twig */
|
||||||
|
$twig = $this->get('twig');
|
||||||
|
|
||||||
|
$config = $twig->render('etc/openvpn-client.conf.twig', [
|
||||||
|
'server' => $server
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $config;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \ZipArchive $zip
|
||||||
|
* @param Certificate $cert
|
||||||
|
*/
|
||||||
|
public function addClientCertificateData($zip, $cert)
|
||||||
|
{
|
||||||
|
$zip->addFromString('client.crt', $cert->getCertificate());
|
||||||
|
|
||||||
|
if ($cert->hasPrivateKey()) {
|
||||||
|
$zip->addFromString('client.key', $cert->getPrivateKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ namespace Eater\Glim\Handler\Panel\Servers\Edit;
|
||||||
|
|
||||||
use Eater\Glim\Handler\Session;
|
use Eater\Glim\Handler\Session;
|
||||||
use Eater\Glim\Model\ServerQuery;
|
use Eater\Glim\Model\ServerQuery;
|
||||||
|
use Eater\Glim\Service\Server;
|
||||||
|
|
||||||
class Action extends Session
|
class Action extends Session
|
||||||
{
|
{
|
||||||
|
@ -23,13 +24,24 @@ class Action extends Session
|
||||||
# Config
|
# Config
|
||||||
$server->setInternalIp($this->post('internal-ip'));
|
$server->setInternalIp($this->post('internal-ip'));
|
||||||
$server->setNetmask($this->post('netmask'));
|
$server->setNetmask($this->post('netmask'));
|
||||||
$server->setPort($this->post('post'));
|
$server->setPort($this->post('port'));
|
||||||
$server->setProtocol($this->post('protocol'));
|
$server->setProtocol($this->post('protocol'));
|
||||||
$server->setFirstDns($this->post('first-dns'));
|
$server->setFirstDns($this->post('first-dns'));
|
||||||
$server->setSecondDns($this->post('second-dns'));
|
$server->setSecondDns($this->post('second-dns'));
|
||||||
|
|
||||||
$server->save();
|
$server->save();
|
||||||
|
|
||||||
$this->json([ 'success'=> true ]);
|
/** @var Server $serverService */
|
||||||
|
$serverService = $this->get('server');
|
||||||
|
try {
|
||||||
|
$serverService->deliverOpenVPNConfig($server);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return $this->json([
|
||||||
|
'success' => false,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->json([ 'success'=> true ]);
|
||||||
}
|
}
|
||||||
}
|
}
|
24
src/Handler/Panel/Servers/Remove.php
Normal file
24
src/Handler/Panel/Servers/Remove.php
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Eater\Glim\Handler\Panel\Servers;
|
||||||
|
|
||||||
|
use Eater\Glim\Handler\Session;
|
||||||
|
use Eater\Glim\Model\ServerQuery;
|
||||||
|
use Eater\Glim\Service\CA;
|
||||||
|
use Eater\Glim\Service\Server;
|
||||||
|
use Symfony\Component\Config\Definition\Exception\Exception;
|
||||||
|
|
||||||
|
class Remove extends Session
|
||||||
|
{
|
||||||
|
protected $shouldHaveSuperuser = true;
|
||||||
|
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$server = ServerQuery::create()->findOneByFingerprint($this->post('fingerprint'));
|
||||||
|
$server->delete();
|
||||||
|
|
||||||
|
return $this->json([
|
||||||
|
'success' => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,10 +17,12 @@ class Sign extends Session
|
||||||
/** @var CA $ca */
|
/** @var CA $ca */
|
||||||
$ca = $this->get('ca');
|
$ca = $this->get('ca');
|
||||||
$server = ServerQuery::create()->findOneByFingerprint($this->post('fingerprint'));
|
$server = ServerQuery::create()->findOneByFingerprint($this->post('fingerprint'));
|
||||||
$publicKey = $server->getPublicKey();
|
|
||||||
|
|
||||||
|
/** @var Server $serverManager */
|
||||||
|
$serverManager = $this->get('server');
|
||||||
try {
|
try {
|
||||||
$crt = $ca->signServerKey($publicKey, $server->getFqdn());
|
$csr = $serverManager->getCsrFromServer($server);
|
||||||
|
$crt = $ca->signServerKey($csr, $server->getFqdn());
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return $this->json([
|
return $this->json([
|
||||||
'success' => false,
|
'success' => false,
|
||||||
|
@ -31,9 +33,6 @@ class Sign extends Session
|
||||||
$server->setCertificate($crt);
|
$server->setCertificate($crt);
|
||||||
$server->save();
|
$server->save();
|
||||||
|
|
||||||
/** @var Server $serverManager */
|
|
||||||
$serverManager = $this->get('server');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$serverManager->deliverSignedCertificate($server);
|
$serverManager->deliverSignedCertificate($server);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
namespace Eater\Glim\Handler;
|
namespace Eater\Glim\Handler;
|
||||||
|
|
||||||
use Aura\Session\Segment;
|
use Aura\Session\Segment;
|
||||||
|
use Eater\Glim\Model\Base\UserQuery;
|
||||||
use Eater\Glim\Model\User;
|
use Eater\Glim\Model\User;
|
||||||
use Eater\Glim\Service\TwigVars;
|
use Eater\Glim\Service\TwigVars;
|
||||||
|
|
||||||
|
@ -48,6 +49,10 @@ class Session extends Main
|
||||||
|
|
||||||
public function beforeHandle()
|
public function beforeHandle()
|
||||||
{
|
{
|
||||||
|
if (!UserQuery::create()->findOne()) {
|
||||||
|
return $this->redirect('/install');
|
||||||
|
}
|
||||||
|
|
||||||
/* @var \Aura\Session\Session */
|
/* @var \Aura\Session\Session */
|
||||||
$session = $this->get('session');
|
$session = $this->get('session');
|
||||||
/** @var TwigVars $twigVar */
|
/** @var TwigVars $twigVar */
|
||||||
|
|
|
@ -16,5 +16,12 @@ use Eater\Glim\Model\Base\Certificate as BaseCertificate;
|
||||||
*/
|
*/
|
||||||
class Certificate extends BaseCertificate
|
class Certificate extends BaseCertificate
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasPrivateKey()
|
||||||
|
{
|
||||||
|
$privateKey = $this->getPrivateKey();
|
||||||
|
return !empty($privateKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,5 +16,8 @@ use Eater\Glim\Model\Base\Server as BaseServer;
|
||||||
*/
|
*/
|
||||||
class Server extends BaseServer
|
class Server extends BaseServer
|
||||||
{
|
{
|
||||||
|
public function getNetmaskIp()
|
||||||
|
{
|
||||||
|
return long2ip(-1 << (32 - (int)$this->getNetmask()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,5 +16,4 @@ use Eater\Glim\Model\Base\User as BaseUser;
|
||||||
*/
|
*/
|
||||||
class User extends BaseUser
|
class User extends BaseUser
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,14 @@ use Eater\Glim\Core;
|
||||||
|
|
||||||
class CA extends Main
|
class CA extends Main
|
||||||
{
|
{
|
||||||
|
public function getPrivateKey()
|
||||||
|
{
|
||||||
|
/** @var Core $core */
|
||||||
|
$core = $this->get('core');
|
||||||
|
|
||||||
|
return file_get_contents($core->getBaseDir() . '/storage/ca/ca.key');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
@ -56,47 +64,14 @@ class CA extends Main
|
||||||
return $crt;
|
return $crt;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $publicKey
|
|
||||||
* @param string $fqdn
|
|
||||||
* @return string
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public function createCSRForKeyAndFqdn($publicKey, $fqdn)
|
|
||||||
{
|
|
||||||
/** @var Core $core */
|
|
||||||
$core = $this->get('core');
|
|
||||||
|
|
||||||
$csrPath = tempnam(sys_get_temp_dir(), '0.');
|
|
||||||
$pubPath = tempnam(sys_get_temp_dir(), '0.');
|
|
||||||
|
|
||||||
file_put_contents($pubPath, $publicKey);
|
|
||||||
|
|
||||||
exec(escapeshellcmd($core->getBaseDir() . '/bin/create-csr') . ' ' . escapeshellarg($fqdn) . ' ' . escapeshellarg($csrPath) . ' ' . escapeshellarg($pubPath) . ' 2>&1', $output, $exitCode);
|
|
||||||
|
|
||||||
if ($exitCode !== 0) {
|
|
||||||
throw new \Exception("Failed creating CSR: " . implode("\n", $output));
|
|
||||||
}
|
|
||||||
|
|
||||||
$csr = file_get_contents($csrPath);
|
|
||||||
|
|
||||||
unlink($pubPath);
|
|
||||||
unlink($csrPath);
|
|
||||||
|
|
||||||
return $csr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signs a client certificate and returns the signed certificate
|
* Signs a client certificate and returns the signed certificate
|
||||||
* @param string $publicKey
|
* @param string $csr
|
||||||
* @param string $fqdn
|
|
||||||
* @return string
|
* @return string
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function signServerKey($publicKey, $fqdn)
|
public function signServerKey($csr)
|
||||||
{
|
{
|
||||||
$csr = $this->createCSRForKeyAndFqdn($publicKey, $fqdn);
|
|
||||||
|
|
||||||
/** @var Core $core */
|
/** @var Core $core */
|
||||||
$core = $this->get('core');
|
$core = $this->get('core');
|
||||||
|
|
||||||
|
@ -197,13 +172,10 @@ class CA extends Main
|
||||||
}
|
}
|
||||||
|
|
||||||
public function signWithCA($data) {
|
public function signWithCA($data) {
|
||||||
/** @var Core $core */
|
$privateKeyPlain = $this->getPrivateKey();
|
||||||
$core = $this->get('core');
|
|
||||||
|
|
||||||
$privateKeyPlain = file_get_contents($core->getBaseDir() . '/storage/ca/ca.key');
|
|
||||||
$privateKey = openssl_get_privatekey($privateKeyPlain);
|
$privateKey = openssl_get_privatekey($privateKeyPlain);
|
||||||
|
|
||||||
$result = openssl_sign($data, $signature, $privateKey);
|
$result = openssl_sign($data, $signature, $privateKey, OPENSSL_ALGO_SHA256);
|
||||||
|
|
||||||
if ($result) {
|
if ($result) {
|
||||||
return $signature;
|
return $signature;
|
||||||
|
|
|
@ -5,6 +5,7 @@ namespace Eater\Glim\Service;
|
||||||
use Eater\Glim\Model\Server as ServerModel;
|
use Eater\Glim\Model\Server as ServerModel;
|
||||||
use GuzzleHttp\Client as HttpClient;
|
use GuzzleHttp\Client as HttpClient;
|
||||||
use Eater\Glim\Core;
|
use Eater\Glim\Core;
|
||||||
|
use Psr\Http\Message\ResponseInterface;
|
||||||
|
|
||||||
class Server extends Main
|
class Server extends Main
|
||||||
{
|
{
|
||||||
|
@ -20,18 +21,52 @@ class Server extends Main
|
||||||
return $this->httpClient;
|
return $this->httpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getCsrFromServer(ServerModel $server) {
|
||||||
|
$response = $this->doSignedRequest($server, '/create-csr', [
|
||||||
|
'hostname' => $server->getFqdn()
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($response->getStatusCode() !== 200) {
|
||||||
|
throw new \Exception('Requesting CSR failed (' . $response->getStatusCode() . '): ' . $response->getBody()->getContents());
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $this->decryptResponse($server, $response);
|
||||||
|
|
||||||
|
return $data['csr'];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ServerModel $server
|
* @param ServerModel $server
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function deliverSignedCertificate(ServerModel $server) {
|
public function deliverSignedCertificate(ServerModel $server) {
|
||||||
$response = $this->doSignedRequest($server, '/deliver-crt', [
|
$response = $this->doSignedRequest($server, '/deliver-crt', [
|
||||||
'certficate' => $server->getCertificate()
|
'certificate' => $server->getCertificate()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($response->getStatusCode() !== 200) {
|
if ($response->getStatusCode() !== 200) {
|
||||||
throw new \Exception('Delivering signed certificate failed (' . $response->getStatusCode() . '): ' . $response->getBody()->getContents());
|
throw new \Exception('Delivering signed certificate failed (' . $response->getStatusCode() . '): ' . $response->getBody()->getContents());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$server->setStatus('signed');
|
||||||
|
$server->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function deliverOpenVPNConfig(ServerModel $server) {
|
||||||
|
/** @var \Twig_Environment $twig */
|
||||||
|
$twig = $this->get('twig');
|
||||||
|
$config = $twig->render('etc/openvpn-server.conf.twig', [
|
||||||
|
'server' => $server
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = $this->doSignedRequest($server, '/update-openvpn-config', [
|
||||||
|
'config' => $config
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($response->getStatusCode() !== 200) {
|
||||||
|
throw new \Exception('Updating openvpn server config failed (' . $response->getStatusCode() . '): ' . $response->getBody()->getContents());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,7 +86,7 @@ class Server extends Main
|
||||||
|
|
||||||
$json = json_encode($data);
|
$json = json_encode($data);
|
||||||
|
|
||||||
$password = bin2hex(openssl_random_pseudo_bytes(32));
|
$password = bin2hex(openssl_random_pseudo_bytes(48));
|
||||||
$pubKey = openssl_get_publickey($server->getPublicKey());
|
$pubKey = openssl_get_publickey($server->getPublicKey());
|
||||||
$success = openssl_public_encrypt($password, $crypted, $pubKey, OPENSSL_PKCS1_PADDING);
|
$success = openssl_public_encrypt($password, $crypted, $pubKey, OPENSSL_PKCS1_PADDING);
|
||||||
|
|
||||||
|
@ -59,19 +94,34 @@ class Server extends Main
|
||||||
throw new \Exception('Encrypting data failed: ' . openssl_error_string() . openssl_error_string());
|
throw new \Exception('Encrypting data failed: ' . openssl_error_string() . openssl_error_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->get('logger')->addDebug('Password: ' . $password);
|
$body = bin2hex($crypted) . bin2hex(openssl_encrypt($json, 'blowfish', substr($password, 0, 32), true, substr($password, 32, 8)));
|
||||||
|
|
||||||
$body = [
|
|
||||||
bin2hex($crypted),
|
|
||||||
bin2hex(openssl_encrypt($server->getCertificate(), 'aes-256-cbc', $password, 'help'))
|
|
||||||
];
|
|
||||||
|
|
||||||
$this->get('logger')->addDebug('Help: ' . var_export([$json, $body], true));
|
|
||||||
|
|
||||||
|
|
||||||
return $client->post('http://' . $server->getExternalIp() . ':' . static::MANAGEMENT_PORT . $path, [
|
return $client->post('http://' . $server->getExternalIp() . ':' . static::MANAGEMENT_PORT . $path, [
|
||||||
'json' => $body
|
'body' => $body
|
||||||
]);
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function decryptResponse(ServerModel $server, ResponseInterface $response) {
|
||||||
|
$body = $response->getBody()->getContents();
|
||||||
|
$this->get('logger')->addDebug($body);
|
||||||
|
$encryptedPasswordAndIV = hex2bin(substr($body, 0, 512));
|
||||||
|
$encryptedBody = hex2bin(substr($body, 512));
|
||||||
|
|
||||||
|
/** @var CA $ca */
|
||||||
|
$ca = $this->get('ca');
|
||||||
|
|
||||||
|
$success = openssl_private_decrypt($encryptedPasswordAndIV, $passwordAndIV, $ca->getPrivateKey(), OPENSSL_PKCS1_PADDING);
|
||||||
|
if (!$success) {
|
||||||
|
throw new \Exception('Decrypting data failed: ' . openssl_error_string() . openssl_error_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
$json = openssl_decrypt($encryptedBody, 'blowfish', substr($passwordAndIV, 0, 32), true, substr($passwordAndIV, 32, 8));
|
||||||
|
$data = \json_decode($json, true);
|
||||||
|
$ver = openssl_verify($server->getFingerprint(), hex2bin($data['signature']), $server->getPublicKey(), OPENSSL_ALGO_SHA256);
|
||||||
|
|
||||||
|
if ($ver === 1) {
|
||||||
|
return $data;
|
||||||
|
} else {
|
||||||
|
throw new \Exception("Checking signature failed ($ver): " . openssl_error_string());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,8 @@ class Twig
|
||||||
$loader = new \Twig_Loader_Filesystem($core->getBaseDir() . '/views/');
|
$loader = new \Twig_Loader_Filesystem($core->getBaseDir() . '/views/');
|
||||||
$twig = new \Twig_Environment($loader, array(
|
$twig = new \Twig_Environment($loader, array(
|
||||||
'cache' => $core->getBaseDir() . '/tmp/twig',
|
'cache' => $core->getBaseDir() . '/tmp/twig',
|
||||||
'debug' => $debug
|
'debug' => $debug,
|
||||||
|
'displayErrorDetails' => $debug
|
||||||
));
|
));
|
||||||
|
|
||||||
if ($twig) {
|
if ($twig) {
|
||||||
|
|
|
@ -24,9 +24,7 @@ class User extends Main
|
||||||
throw new \Exception("Invalid invite code");
|
throw new \Exception("Invalid invite code");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->exists($username)) {
|
$this->validateUserParams($username, $password);
|
||||||
throw new \Exception("User already exists");
|
|
||||||
}
|
|
||||||
|
|
||||||
$user = new UserModel();
|
$user = new UserModel();
|
||||||
$user->setUsername($username);
|
$user->setUsername($username);
|
||||||
|
@ -38,6 +36,41 @@ class User extends Main
|
||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function validateUserParams($username, $password) {
|
||||||
|
if ($username === "") {
|
||||||
|
throw new \Exception("No username given");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!preg_match('~^[a-z0-9\-]+$~', $username)) {
|
||||||
|
throw new \Exception("Username can only consist of a-z, 0-9 and -");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($password === "") {
|
||||||
|
throw new \Exception("Password is nothing, though strong. we rather not have you use that");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($password) < 9) {
|
||||||
|
throw new \Exception("Please pick a password with more then 8 characters");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->exists($username)) {
|
||||||
|
throw new \Exception("User already exists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createSuperuser($username, $password) {
|
||||||
|
|
||||||
|
$this->validateUserParams($username, $password);
|
||||||
|
|
||||||
|
$user = new UserModel();
|
||||||
|
$user->setUsername($username);
|
||||||
|
$user->setPassword(\password_hash($password, PASSWORD_DEFAULT));
|
||||||
|
$user->setSuperuser(true);
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $username
|
* @param string $username
|
||||||
* @return bool
|
* @return bool
|
||||||
|
|
|
@ -6,15 +6,14 @@ default_ca=ca_default
|
||||||
[v3_ca]
|
[v3_ca]
|
||||||
[ca_default]
|
[ca_default]
|
||||||
crl_extensions=crl_ext
|
crl_extensions=crl_ext
|
||||||
unique_subject=no
|
private_key=storage/ca/ca.key
|
||||||
private_key=storage/ca.key
|
certificate=storage/ca/ca.crt
|
||||||
certificate=storage/ca.crt
|
new_certs_dir=storage/ca/certs/
|
||||||
new_certs_dir=storage/certs/
|
database=storage/ca/database
|
||||||
database=storage/database
|
|
||||||
default_md=sha256
|
default_md=sha256
|
||||||
policy=policy_only_commonname
|
policy=policy_only_commonname
|
||||||
serial=storage/serial
|
serial=storage/ca/serial
|
||||||
crlnumber=storage/crl_serial
|
crlnumber=storage/ca/crl_serial
|
||||||
default_crl_days=1
|
default_crl_days=1
|
||||||
[policy_only_commonname]
|
[policy_only_commonname]
|
||||||
countryName = optional
|
countryName = optional
|
||||||
|
@ -32,14 +31,14 @@ subjectKeyIdentifier=hash
|
||||||
authorityKeyIdentifier = keyid,issuer:always
|
authorityKeyIdentifier = keyid,issuer:always
|
||||||
extendedKeyUsage = serverAuth
|
extendedKeyUsage = serverAuth
|
||||||
keyUsage = digitalSignature,keyEncipherment
|
keyUsage = digitalSignature,keyEncipherment
|
||||||
crlDistributionPoints = URI:http://{{ hostname }}/crl
|
crlDistributionPoints = URI:http://{{ host }}/crl
|
||||||
[client_ext]
|
[client_ext]
|
||||||
subjectKeyIdentifier=hash
|
subjectKeyIdentifier=hash
|
||||||
basicConstraints = CA:FALSE
|
basicConstraints = CA:FALSE
|
||||||
crlDistributionPoints = URI:http://{{ hostname }}/crl
|
crlDistributionPoints = URI:http://{{ host }}/crl
|
||||||
[ca_ext]
|
[ca_ext]
|
||||||
basicConstraints = CA:TRUE
|
basicConstraints = CA:TRUE
|
||||||
subjectKeyIdentifier=hash
|
subjectKeyIdentifier=hash
|
||||||
crlDistributionPoints = URI:http://{{ hostname }}/crl
|
crlDistributionPoints = URI:http://{{ host }}/crl
|
||||||
[crl_ext]
|
[crl_ext]
|
||||||
authorityKeyIdentifier=keyid:always
|
authorityKeyIdentifier=keyid:always
|
27
views/etc/openvpn-client.conf.twig
Normal file
27
views/etc/openvpn-client.conf.twig
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
client
|
||||||
|
|
||||||
|
dev zerooo
|
||||||
|
dev-type tun
|
||||||
|
|
||||||
|
proto {{ server.getProtocol() }}
|
||||||
|
|
||||||
|
remote {{ server.getExternalIp() }} {{ server.getPort() }}
|
||||||
|
resolv-retry infinite
|
||||||
|
nobind
|
||||||
|
|
||||||
|
user nobody
|
||||||
|
group nogroup
|
||||||
|
|
||||||
|
persist-key
|
||||||
|
persist-tun
|
||||||
|
|
||||||
|
ca ca.crt
|
||||||
|
cert client.crt
|
||||||
|
key client.key
|
||||||
|
|
||||||
|
remote-cert-tls server
|
||||||
|
|
||||||
|
cipher BF-CBC
|
||||||
|
|
||||||
|
comp-lzo
|
||||||
|
verb 3
|
32
views/etc/openvpn-server.conf.twig
Normal file
32
views/etc/openvpn-server.conf.twig
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
port {{ server.getPort() }}
|
||||||
|
proto {{ server.getProtocol() }}
|
||||||
|
|
||||||
|
dev ovpn0
|
||||||
|
dev-type tun
|
||||||
|
|
||||||
|
ca ca.crt
|
||||||
|
cert server.crt
|
||||||
|
key server.key
|
||||||
|
|
||||||
|
dh dh2048.pem
|
||||||
|
|
||||||
|
server {{ server.getInternalIp() }} {{ server.getNetmaskIp() }}
|
||||||
|
|
||||||
|
ifconfig-pool-persist ipp.txt
|
||||||
|
|
||||||
|
push "redirect-gateway def1 bypass-dhcp"
|
||||||
|
|
||||||
|
push "dhcp-option DNS {{ server.getFirstDns() }}"
|
||||||
|
push "dhcp-option DNS {{ server.getSecondDns() }}"
|
||||||
|
keepalive 10 120
|
||||||
|
cipher BF-CBC
|
||||||
|
|
||||||
|
crl-verify crl.pem
|
||||||
|
|
||||||
|
comp-lzo
|
||||||
|
|
||||||
|
user nobody
|
||||||
|
group nogroup
|
||||||
|
|
||||||
|
persist-key
|
||||||
|
persist-tun
|
80
views/install.html.twig
Normal file
80
views/install.html.twig
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
{% extends "base.html.twig" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container">
|
||||||
|
<div class="col-md-offset-3 col-md-6">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="row">
|
||||||
|
<h2>Welcome to your Zer.ooo install</h2>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<h3>Checking for extensions and settings</h3>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<table class="table">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>{{ hasExecEnabled ? 'Yes' : 'No' }}</td>
|
||||||
|
<td>Is <kbd>exec</kbd> available?</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ hasOpenSsl ? 'Yes' : 'No' }}</td>
|
||||||
|
<td>Is the <kbd>openssl</kbd> binary available?</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ hasOpenSslExtension ? 'Yes' : 'No' }}</td>
|
||||||
|
<td>Is the openssl module loaded?</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ hasZipExtension ? 'Yes' : 'No' }}</td>
|
||||||
|
<td>Is the zip module loaded?</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if not (hasExecEnabled and hasOpenSslExtension and hasOpenSsl and hasZipExtension) %}
|
||||||
|
<div class="row">
|
||||||
|
Those functions are essential for the functionality of Zer.ooo, please enable them<br>
|
||||||
|
<br>
|
||||||
|
<a href="/install" class="btn btn-primary">Refresh</a>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="row">
|
||||||
|
<h3>Create your superuser</h3>
|
||||||
|
</div>
|
||||||
|
<form method="post" action="/install">
|
||||||
|
<div class="row">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="username">Username</label>
|
||||||
|
<input id="username" class="form-control" type="text" name="username">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="password">Password</label>
|
||||||
|
<input id="password" class="form-control" type="password" name="password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<h3>Server details</h3>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="ca-cn">CA Common Name</label>
|
||||||
|
<input type="text" id="ca-cn" name="ca-cn" class="form-control" value="{{ hostname }}" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="domainWithPort">HTTP Host (NOT HTTPS! needed for CRL)</label>
|
||||||
|
<input type="text" id="domainWithPort" name="domainWithPort" class="form-control" value="{{ hostnameWithPort }}" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<button type="submit" class="btn btn-primary pull-right">Finish install</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -8,7 +8,9 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
<h2 id="certificates">Certificates</h2>
|
<h2 id="certificates">Certificates</h2>
|
||||||
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -53,7 +55,10 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
<h2>Servers</h2>
|
<h2>Servers</h2>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -65,7 +70,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for server in servers %}\
|
{% for server in servers %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
{{ server.getFqdn() }}
|
{{ server.getFqdn() }}
|
||||||
|
@ -81,7 +86,27 @@
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
|
<div class="btn-group">
|
||||||
|
<a href="/panel/server/{{ server.getFingerprint() }}/config" class="btn btn-default">Download config</a>
|
||||||
|
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
|
<span class="caret"></span>
|
||||||
|
<span class="sr-only">Toggle Dropdown</span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
{% for certificate in user.getCertificates() if certificate.getRevoked() == 0 %}
|
||||||
|
<li>
|
||||||
|
<a href="/panel/server/{{ server.getFingerprint() }}/config/{{ certificate.getName() }}">Download config for "{{ certificate.getName() }}"</a>
|
||||||
|
</li>
|
||||||
|
{% else %}
|
||||||
|
<li>
|
||||||
|
<a>You dont have any certificates</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% if user.isSuperUser() %}
|
||||||
|
<a href="/panel/server/{{ server.getFingerprint() }}" class="btn btn-default">Edit</a>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -94,8 +119,12 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
{% if user.getSuperuser() %}
|
{% if user.getSuperuser() %}
|
||||||
|
<div class="row">
|
||||||
<h2>Registered servers</h2>
|
<h2>Registered servers</h2>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -115,7 +144,8 @@
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<a href="/panel/server/{{ server.getFingerprint() }}" class="btn btn-default">Details</a>
|
<button data-fingerprint="{{ server.getFingerprint() }}" class="remove btn btn-warning">Remove</button>
|
||||||
|
<a href="/panel/server/{{ server.getFingerprint() }}" class="btn btn-default">Edit</a>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -128,6 +158,7 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="modal fade in revoke-modal" tabindex="-1" role="dialog">
|
<div class="modal fade in revoke-modal" tabindex="-1" role="dialog">
|
||||||
|
|
|
@ -83,7 +83,9 @@
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<button type="button" class="btn save">Save</button>
|
<button type="button" class="btn save">Save</button>
|
||||||
|
{% if server.getStatus() == 'registered' %}
|
||||||
<button type="button" class="btn btn-primary save-and-sign">Sign and save</button>
|
<button type="button" class="btn btn-primary save-and-sign">Sign and save</button>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue