Last active
September 25, 2021 14:30
-
-
Save ZebTheWizard/278b65fb852322515dbdc00a18f340ba to your computer and use it in GitHub Desktop.
Cpanel git bare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// 1. place this install script in your public_html/ folder | |
// 2. go to example.com/install.php to generate all the files | |
// 3. add basic authentication users by clicking "manager users" | |
// 4. add the new remote "example.com/repos/git.cgi/<repo>.git" | |
// 5. remove the install.php script if it still exists. | |
ini_set('display_errors', 1); | |
ini_set('display_startup_errors', 1); | |
error_reporting(E_ALL); | |
?> | |
<?php if (isset($_POST["manage_users"])) : ?> | |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Git HTTP Installer</title> | |
<style> | |
section { | |
margin-bottom: 1rem; | |
} | |
</style> | |
</head> | |
<body> | |
<h1> | |
Git HTTP Installer for cPanel sites | |
</h1> | |
<div> | |
<a href="">Go back</a> | |
</div> | |
<p>You may need to contact your cPanel administrator to have htpasswd enabled.</p> | |
<div> | |
<strong style="color: red;"> This will wipe all current .htpasswd users</strong> | |
</div> | |
<form action="" method="POST"> | |
<button type="submit" name="users_submitted" value="true">Save</button> | |
<section> | |
<label for="usernames"> | |
<div>Usernames</div> | |
<div><small>Line separated list of usernames with access to repo.</small></div> | |
<textarea type="text" name="usernames" id="usernames" placeholder="bob | |
alice" value=""></textarea> | |
</label> | |
</section> | |
<section> | |
<label for="passwords"> | |
<div>Passwords</div> | |
<div><small>Line separated list of passwords for corresponding users in field above.</small></div> | |
<textarea type="text" name="passwords" id="passwords" placeholder="suP3rs3cr3T | |
t0t@llyS@fe" value=""></textarea> | |
</label> | |
</section> | |
<input type="hidden" name="submitted" value="true"> | |
<button type="submit" name="users_submitted" value="true">Save</button> | |
</form> | |
</body> | |
</html> | |
<?php elseif (isset($_POST['submitted'])) : ?> | |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Installing</title> | |
</head> | |
<body> | |
<h1>Installing...</h1> | |
<div> | |
<a href="">Go back</a> | |
</div> | |
</body> | |
</html> | |
<?php else : ?> | |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Git HTTP Installer</title> | |
<style> | |
section { | |
margin-bottom: 1rem; | |
} | |
</style> | |
</head> | |
<body> | |
<h1> | |
Git HTTP Installer for cPanel sites | |
</h1> | |
<p>You may need to contact your cPanel administrator to have git enabled.</p> | |
<form action="" method="POST"> | |
<button type="submit" name="clean" value="true">Clean</button> | |
<button type="submit" name="manage_users" value="true">Manage Users</button> | |
<button type="submit" name="settings_submitted" value="true">Install</button> | |
<section> | |
<label for="domain"> | |
<div>Domain Name</div> | |
<div><small>Do not include www.</small></div> | |
<input type="text" name="domain" id="domain" placeholder="example.com" value="<?php echo $_SERVER['SERVER_NAME'] ?>"> | |
</label> | |
</section> | |
<section> | |
<label for="repo"> | |
<div>Repo Name</div> | |
<div><small>Legal file system characters.</small></div> | |
<input type="text" name="repo" id="repo" placeholder="production" value="production"> | |
</label> | |
</section> | |
<section> | |
<label for="basedir"> | |
<div>Base Directory</div> | |
<div><small>Directory relative to repo where index.php is located.</small></div> | |
<input type="text" name="basedir" id="basedir" placeholder="/public" value="/"> | |
</label> | |
</section> | |
<section> | |
<label for="branch"> | |
<div>Branch</div> | |
<div><small>The branch to deploy</small></div> | |
<input type="text" name="branch" id="branch" placeholder="master" value="master"> | |
</label> | |
</section> | |
<input type="hidden" name="submitted" value="true"> | |
<button type="submit" name="settings_submitted" value="true">Install</button> | |
</form> | |
</body> | |
</html> | |
<?php endif ?> | |
<?php | |
class Shell | |
{ | |
private static $commands = []; | |
public static function exec_shell($cmd) | |
{ | |
$handle = popen("$cmd 2>&1", "r"); | |
$output = ""; | |
while (!feof($handle)) { | |
$buffer = fgets($handle, 4096); | |
$output .= $buffer; | |
} | |
pclose($handle); | |
return $output; | |
} | |
private static function print_shell($cmd) | |
{ | |
$output = static::exec_shell($cmd); | |
echo "<div style='margin-bottom: 1rem; border-bottom: 1px solid black; padding-bottom: 1rem;'> | |
<pre>> $cmd</pre> | |
<div style='padding-left: 1rem'><pre>$output</pre></div></div>"; | |
} | |
public static function run($cmd) | |
{ | |
static::print_shell($cmd); | |
} | |
public static function start() | |
{ | |
ob_start(); | |
} | |
public static function end() | |
{ | |
$commands = explode(PHP_EOL, ob_get_clean()); | |
$sub = "("; | |
$inSub = false; | |
foreach ($commands as $command) { | |
$command = trim($command); | |
if ($command === "(") { | |
$inSub = true; | |
} else if ($inSub) { | |
if ($command === ")") { | |
$sub .= ")"; | |
static::exec_shell($sub); | |
$sub = "("; | |
$inSub = false; | |
} else { | |
$sub .= $command . ";"; | |
} | |
} else if (!empty($command)) { | |
static::exec_shell($command); | |
} | |
} | |
} | |
} | |
class IO | |
{ | |
private static $writeLocation = ""; | |
public static function write($location, $permissions, $contents = null) | |
{ | |
Shell::run("touch $location && chmod $permissions $location"); | |
static::$writeLocation = $location; | |
if (empty($contents)) { | |
ob_start(); | |
} else { | |
file_put_contents($location, $contents); | |
} | |
} | |
public static function writeEnd() | |
{ | |
$buffer = ob_get_clean(); | |
file_put_contents(static::$writeLocation, $buffer); | |
static::$writeLocation = ""; | |
} | |
public static function die($message) | |
{ | |
echo "<div><strong>ERROR: </strong> $message<br></div>"; | |
die(); | |
} | |
public static function guard_post($field) | |
{ | |
if (!IO::has_post($field)) IO::die("missing $field field"); | |
return IO::post($field); | |
} | |
public static function has_post($key) | |
{ | |
return isset($_POST[$key]) && strlen(trim($_POST[$key])); | |
} | |
public static function post($key) | |
{ | |
return static::has_post($key) ? trim($_POST[$key]) : null; | |
} | |
public static function redirect($url) | |
{ | |
$string = '<script type="text/javascript">'; | |
$string .= 'window.location = "' . $url . '"'; | |
$string .= '</script>'; | |
echo $string; | |
} | |
public static function removeDirectory($dir) | |
{ | |
if (!file_exists($dir)) { | |
return true; | |
} | |
if (!is_dir($dir)) { | |
return unlink($dir); | |
} | |
foreach (scandir($dir) as $item) { | |
if ($item == '.' || $item == '..') { | |
continue; | |
} | |
if (!static::removeDirectory($dir . DIRECTORY_SEPARATOR . $item)) { | |
return false; | |
} | |
} | |
return rmdir($dir); | |
} | |
} | |
?> | |
<?php | |
if (isset($_POST['settings_submitted'])) { | |
$DOMAIN = IO::guard_post("domain"); | |
$REPO = IO::guard_post("repo"); | |
$BASEDIR = IO::guard_post("basedir"); | |
$BRANCH = IO::guard_post("branch"); | |
$pwd = getcwd(); | |
$repoLocation = $REPO . $BASEDIR; | |
Shell::run("mkdir -p code/$REPO"); | |
Shell::run("mkdir -p repos"); | |
Shell::run("mkdir -p repo_data/$REPO.git"); | |
Shell::run("git init --bare --shared=all repo_data/$REPO.git"); | |
Shell::run("chmod -R g+rw repo_data/$REPO.git"); | |
Shell::run("chgrp -R theplace repo_data/$REPO.git"); | |
// htpasswd -db /usr/web/.htpasswd-all jones Pwd4Steve | |
IO::write(".htaccess", "644", "# GENERATED HTACCESS CODE | |
RewriteEngine on | |
RewriteCond %{HTTP_HOST} ^(www.)?$DOMAIN$ | |
RewriteCond %{REQUEST_URI} !^/code/$repoLocation | |
RewriteCond %{REQUEST_FILENAME} !-f | |
RewriteCond %{REQUEST_FILENAME} !-d | |
RewriteRule ^(.*)$ /code/$repoLocation$1 | |
RewriteCond %{HTTP_HOST} ^(www.)?$DOMAIN$ | |
RewriteRule ^(/)?$ code/$repoLocation" . "index.php [L] | |
# END OF GENERATED HTACCESS CODE"); | |
IO::write("repo_data/.htaccess", "644", "deny from all"); | |
IO::write("repos/.htaccess", "644", "Options +ExecCGI | |
AddHandler cgi-script cgi | |
SetEnv GIT_PROJECT_ROOT $pwd/repo_data/ | |
SetEnv GIT_HTTP_EXPORT_ALL | |
SetEnv BACKDOOR_INSTALL_LOCATION $pwd | |
#ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/ | |
# AUTHENTICATION | |
AuthType Basic | |
AuthName \"Protected '$pwd/repos'\" | |
AuthUserFile \"$pwd/.htpasswd\" | |
Require valid-user"); | |
IO::write("repos/git.cgi", "755", "#! /bin/sh | |
logfile=$pwd/repos/git.log | |
date >> \"\$logfile\" | |
git http-backend \"\$@\" 2>> \"\$logfile\" || echo failed >> \"\$logfile\" | |
echo >> \"\$logfile\""); | |
IO::write("repos/git.log", "622", ""); | |
IO::write("checkout_latest_version.php", "755", "<?php | |
function run(\$cmd) | |
{ | |
\$handle = popen(\"\$cmd 2>&1\", 'r'); | |
\$output = fread(\$handle, 4096); | |
pclose(\$handle); | |
return \$output; | |
} | |
\$user = get_current_user(); | |
\$REPO = '$REPO'; | |
\$BRANCH = '$BRANCH'; | |
\$pwd = '$pwd'; | |
echo ('Checking out remote as :' . \"\$user\\n\"); | |
echo run(\"git --work-tree=\$pwd/code/\$REPO --git-dir=\$pwd/repo_data/\$REPO.git checkout -f \$BRANCH\"); | |
"); | |
IO::write("repo_data/$REPO.git/hooks/post-receive", "755", "#!/bin/sh | |
#Always works | |
curl $DOMAIN/checkout_latest_version.php | |
#Does not work when cgi scripts execute as nobody. | |
#php $pwd/repos/checkout.php"); | |
Shell::run("chmod -R 777 repo_data/$REPO.git"); | |
Shell::run("chmod -R 777 code"); | |
Shell::run("ls -al repo_data/$REPO.git"); | |
echo "<div><code><pre>git remote add $REPO https://$DOMAIN/repos/git.cgi/$REPO.git</pre></code></div>"; | |
} | |
if (isset($_POST['users_submitted'])) { | |
$usernamesText = IO::guard_post("usernames"); | |
$passwordsText = IO::guard_post("passwords"); | |
$usernames = array_filter(explode(PHP_EOL, $usernamesText), "strlen"); | |
$passwords = array_filter(explode(PHP_EOL, $passwordsText), "strlen"); | |
if (count($usernames) != count($passwords)) IO::die("number of usernames and passwords do not match."); | |
for ($i = 0; $i < count($usernames); $i++) { | |
$username = $usernames[$i]; | |
$password = $passwords[$i]; | |
Shell::run("htpasswd -c -m -b .htpasswd $username $password"); | |
} | |
} | |
if (isset($_POST['clean'])) { | |
$REPO = IO::guard_post("repo"); | |
$pwd = getcwd(); | |
IO::removeDirectory("code/$REPO"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment