Skip to content
This repository has been archived by the owner on Jun 25, 2020. It is now read-only.

New Design with PHP 7 support #5

Open
thatsamguy opened this issue Sep 7, 2015 · 8 comments
Open

New Design with PHP 7 support #5

thatsamguy opened this issue Sep 7, 2015 · 8 comments

Comments

@thatsamguy
Copy link
Owner

As per #3 @markc we can discuss here about the new changes.

I'm currently using PHP 5.4 in my prod system, but I'm likely to keep that running the current Albatross version (now tagged and branched as cyprix-pre2015) on that one and replace it with a new server with different specs and the new setup based on master.

As such, I think PHP 5.5 is a sensible earliest version, with PHP 7 the prime target.

I'll admit some of the code is cringe worthy (like the globals and coding style - PSR-2 is a good choice). All this code and it's design dates back primarily to around 3-5 years ago when I was a young padawan in the industry (we all have to learn somehow ;) ).

Currently there are no roles within albatross itself, other than giving a user sudo privileges manually. The closest to that in albatross is the password override hack (feel free to remove this), where you can login as any user with a superpassword $conf['rootpassword'].

There is no UI currently for adding users. However you can use the code below to send a new user create job to the amm daemon (which really needs root privileges). The "100001" is the ID of the user performing the action - but is just a reference for account creation and is not checked (no admin roles).

<?php
set_include_path('lib:conf');
include("amm.class.php");
$amm = new amm();

$jobdata = array("username"=>"someUsername","password"=>"blahblahblah");
$add_job = $amm->add_job("Account-CreateAccount","100001",$jobdata);
if(!$add_job[0]){
  $error->add("test.php","whoa! fail: ".$add_job[1]);
}
else {
  echo "JobID: $add_job[1]";
}

The success of the job can be checked either via mysql amm table (E.g. mysql albatross -e "SELECT * FROM amm WHERE uid='1441663549_543'") or the amm logs for full details.

@markc
Copy link
Contributor

markc commented Sep 8, 2015

Great, so PHP 5.5 can be the minimum. That means using the short array syntax is feasible...

As of PHP 5.4 you can also use the short array syntax, which replaces array() with [].

My first pull request was two simple changes to allow the code to run under php7 but my next pull request will be huge because I'd like to use https://github.com/FriendsOfPHP/PHP-CS-Fixer to mass convert the entire codebase to psr-2 and manually patch up any glitches.

@thatsamguy
Copy link
Owner Author

I saw both changes were in the same PR, but I've merged #7 now.

@markc
Copy link
Contributor

markc commented Sep 8, 2015

FWIW #9 was created with this and should only be temporary (GNU sed only)...

find -name "*.php" -exec sed -i '1 i\<?php error_log(__FILE__);?>' {} \;

and can be removed with...

find -name "*.php" -exec sed -i '1d' {} \;

@thatsamguy
Copy link
Owner Author

Following up from your comments in #9, I've added some initial backend docs (including the sql schema) for powerdns, dovecot and postifx in #10.

I'm aware of your lack of intent to use the dovecot and postifx parts - no problem with that (I like courier too!) - but felt this would in conjunction with the missing schemas (my bad) would hopefully give a better idea with what was going on Albatross MySQL side.

UI wise, there are three main components handled separately.

  • Sites (nginx & php config, files on disk)
  • Domains (stored in mysql, used by powerdns)
  • Databases (created in mysql)
  • Email (domains, users and aliases stored in mysql for currently postfix/dovecot

You can configure each of these separately and in any order you like. As long as in the end you have DNS pointing to the correct web and mail servers, the Site (web) and Email (mail) setup matches the domain and the Database created is set correctly in your apps php code all should work ok.

I tried to design this more from a users point of view instead of a sysadmin/operations one.

I.e.:

  • I have a domain. Point it to the cyprix nameservers. Login to albatross. Add domain. Add subdomain for site.
  • I need a database for a site. Login to albatross. Create a database.
  • I need a site. Login to albatross. Add a site. What kind of site? Wordpress. Select wordpress profile. Choose a funky name. What domain? the subdomain set in domains. Create site. Login via SFTP/SCP. Copy files to folder named the same as the site I just created. Change php to link to the new database.
  • I need email. Login to albatross. Add some MX records for my email domain. Add email for my domain. Add some users and aliases.

@thatsamguy
Copy link
Owner Author

From a design point of view, I sought to get around the privilege problem of not wanting web based process being able to do system level stuff. So the model was created where the web UI would send jobs to a queue where a root/system level daemon also written in php (amm.php) which would take jobs, thread off a process that would validate data and attempt to do the work, then report back to mysql the result.

As such items such as "create an account" are added to the amm queue, which triggers the jobpack "Account-CreateAccount". In there, the data is validated first for safety, then attempts the account classes "add" function. Upon success it then issues new jobs "Account-CreateHomeDir" to create the home directory and "Site-ConfPhpFpm" to configure up php-fpm for the user. The job then gets marked as closed. amm.php would then see the two new jobs and act upon them, even trying up to four times upon failure.

amm.php by default is set to handle up to 5 jobs simultaneously.

@markc
Copy link
Contributor

markc commented Sep 8, 2015

Thanks for your input, I'll look at your docs and reread the above two posts again tomorrow. The amm.php provisioning system seems to be a pretty good compromise between a simple exec(sudo something) and using 1 minute cronjobs with root perms. I'm leaning towards using php-ssh2 when it becomes available for php7, or phpseclib in the meantime, to provide that abstraction layer between PHP and root access. Using sshkeys on a passwordless root account on the same server is fairly efficient and the overall strategy has one hell of an awesome benefit in that only one line of code needs to be changed for the target server to provision or activate the same service on any remote server.

Two codebase related points FWIW... globals and goto's really should be... shot on sight :-) With php5.5+ there are so many nicer ways to achieve the same end result with closures and anonymous functions, and just old school functions and classes for that matter. One of the reasons I like your codebase is that it is not polluted with some over-engineered framework and/or template engine, however, some thought on removing the goto's first then globals could be time well spent. That leads me to the 2nd point which is there is a lot of repeated boilerplate code that could be seriously refactored. I'd say at a guess that this...

find -name "*.php" -exec cat {} \; | wc
15689   44196  500507

could be reduced to at least half that size with a bit more of a DRY approach.

@thatsamguy
Copy link
Owner Author

Completely agree on the globals, gotos and boilerplate code. Just had not spent the time to do it in the past - had focused on stabilty and minor feature improvements before the 2 year lull with no changes.

@markc
Copy link
Contributor

markc commented Sep 8, 2015

I just tested a ping test via ssh from nginx with each ping line being echoed back in real time. If you want to try this then somehow get this below to /usr/share/php/phpseclib...

https://github.com/phpseclib/phpseclib/tree/master/phpseclib

and add a id_rsa.pub to roots ~/.ssh/authorized_keys and put the id_rsa somewhere your webserver can get at it (and chmod 600) then make sure nginx's gzip is turned off...

fastcgi_keep_conn on;
proxy_buffering off;
gzip off;

and then surf to somewhere this file is stored...

~ cat pingtest.php
<?php
const TARGET = 'example.org';
const SSHKEY = '/home/www-data/.ssh/id_rsa';

function __autoload($inc) {
  if ($inc == 'Sodium') return;
  include '/usr/share/php/'.str_replace('\\', '/', $inc).'.php';
}

$ssh = new \phpseclib\Net\SSH2(TARGET);
$key = new \phpseclib\Crypt\RSA();
$key->loadKey(file_get_contents(SSHKEY));

if (!$ssh->login('root', $key)) {
    exit('Login Failed');
}

echo '<pre>';
$ssh->exec('ping -c10 127.0.0.1', function ($str) { echo $str; flush(); ob_flush(); });
echo '</pre>';

Update: I just tried the same script above but with a remote server and changed the ping target to point back to my local router and it worked. The only gotcha was that I had to su - www-data first and manually do a ssh to the same server to get past the accept-unknown-key prompt. Once I had a ~/www-data/.ssh/known_hosts file then it worked from a browser. So I basically changed one line and the same ping test script worked on 2 different servers. The ob_flush() throws a Warning when testing the same script from a shell.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants