Last active
October 17, 2018 13:36
-
-
Save stampycode/0ba2f54c4e7b118fc879ab378bf2b3d8 to your computer and use it in GitHub Desktop.
Interactive browser for a Docker Registry API (v2)
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 | |
/** | |
* Browse a specified (v2) Docker Registry, examine layers, extract useful data >.> | |
**/ | |
ini_set('error_reporting', E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED & ~E_WARNING); | |
$headers = []; | |
$type = isset($_GET['type']) ? $_GET['type'] : '_catalog'; | |
$registry = (isset($_GET['registry'])) ? $_GET['registry'] : ''; | |
$tag = isset($_GET['tag']) ? $_GET['tag'] : ''; | |
$repo = isset($_GET['repo']) ? $_GET['repo'] : ''; | |
if(!$registry) { | |
echo "registry query param is required"; | |
die; | |
} | |
$delete = $digestsLink = ''; | |
$pre = true; | |
if($type === '_catalog') { | |
$url = "/$type?n=1000"; | |
} | |
elseif($type === 'repo') { | |
$url = "/$repo/tags/list"; | |
} | |
elseif($type === 'tag') { | |
$url = "/$repo/manifests/$tag"; | |
} | |
elseif($type === 'tagdigests') { | |
$url = "/$repo/manifests/$tag"; | |
$headers[] = 'Accept: application/vnd.docker.distribution.manifest.v2+json'; | |
} | |
elseif($type === 'blob') { | |
$digest = $_GET['digest']; | |
$url = "/$repo/blobs/$digest"; | |
$headers[] = 'Range: bytes=0-4096'; | |
} | |
if(isset($_SERVER['PHP_AUTH_USER'])) { | |
$auth = base64_encode($_SERVER['PHP_AUTH_USER'] . ':' . $_SERVER['PHP_AUTH_PW']); | |
$headers[] = "Authorization: Basic $auth"; | |
} | |
//DELETE a TAG | |
// DELETE /v2/<name>/manifests/<reference> | |
$host = "https://$registry/v2"; | |
$context = stream_context_create( | |
[ | |
'http' => [ | |
'method' => $_SERVER['REQUEST_METHOD'], | |
'header' => implode("\r\n", $headers), | |
], | |
] | |
); | |
$data = file_get_contents($host . $url, false, $context); | |
header($http_response_header[0]); | |
if($http_response_header[0] === 'HTTP/1.1 401 Unauthorized') { | |
header('WWW-Authenticate: Basic realm="User Visible Realm"'); | |
return; | |
} | |
if($_SERVER['REQUEST_METHOD'] !== 'GET') { | |
header('content-type: text/json'); | |
print_r($http_response_header); | |
echo $url."\n"; | |
echo $data; | |
exit; | |
}; | |
header('content-type: text/html'); | |
if($type !== 'blob') { | |
$data = json_decode($data, 1); | |
} | |
if($type === '_catalog') { | |
foreach($data['repositories'] as &$repo) { | |
$repo = "<a href='?registry=$registry&type=repo&repo=$repo'>$repo</a>"; | |
} | |
} | |
elseif($type === 'repo') { | |
usort( | |
$data['tags'], | |
function($a, $b) { | |
return version_compare($a, $b); | |
} | |
); | |
foreach($data['tags'] as &$tag) { | |
$tag = "<a href='?registry=$registry&type=tag&repo=$repo&tag=$tag'>$tag</a>"; | |
} | |
} | |
elseif($type === 'tag') { | |
$digestsLink = "<a href='?registry=$registry&type=tagdigests&repo=$repo&tag=$tag&showdigests=1'>[see digests]</a>"; | |
foreach($data['history'] as &$hist) { | |
$hist['v1Compatibility'] = json_decode($hist['v1Compatibility'], 1); | |
} | |
foreach($data['fsLayers'] as &$fsLayer) { | |
$fsLayer['blobSum'] = "<a href='?registry=$registry&type=blob&repo=$repo&digest={$fsLayer['blobSum']}'>{$fsLayer['blobSum']}</a>"; | |
} | |
} | |
elseif($type === 'tagdigests') { | |
$href = "?registry=$registry&type=tag&repo=$repo&tag=". urlencode($data['config']['digest']); | |
$delete = "<a class='delete' href='$href' data-tag='$tag'>[DELETE TAG]</a>"; | |
} | |
elseif($type === 'blob') { | |
$tempfile = tempnam('/tmp', 'blob'); | |
file_put_contents($tempfile, $data); | |
$data = `/bin/cat $tempfile |gzip -d`; | |
unlink($tempfile); | |
$data = str_split($data, 72); | |
$data = str_split($data, 72); | |
$color = '#888'; | |
$placeholder = '.'; | |
$repl = function($m) use (&$color, &$placeholder) { | |
$x = str_repeat($placeholder, strlen($m[0])); | |
return '<span style="background-color:'.$color.';">'.$x.'</span>'; | |
}; | |
foreach($data as &$d) { | |
$d = str_replace(['<','>'], ['<','>'], $d); | |
$d = preg_replace_callback('/\x00+/', $repl, $d); | |
$color = '#f88'; | |
$d = preg_replace_callback('/[\x01-\x0f]+/', $repl, $d); | |
$color = '#aaf'; | |
$d = preg_replace_callback('/[\x10-\x1f]+/', $repl, $d); | |
$color = '#fa0'; | |
$d = preg_replace_callback('/[\x7f-\xff]+/', $repl, $d); | |
$color = '#ccc'; | |
$d = preg_replace_callback('/[^\x20-\x7f]+/', $repl, $d); | |
} | |
$data = implode('<br>', $data); | |
$pre = false; | |
} | |
$prt = function($data, $depth=1) use (&$prt) { | |
if(!is_array($data)) { | |
return "'$data'"; | |
} | |
$out = '{'. "\n"; | |
foreach($data as $k => $d) { | |
$out .= str_repeat(' ', $depth*4) . "$k: " . $prt($d, $depth + 1) ."\n"; | |
} | |
$out .= str_repeat(' ', ($depth-1)*4) . '}'. "\n"; | |
return $out; | |
}; | |
?> | |
<!DOCTYPE html> | |
<html><head> | |
<meta charset="UTF-8"> | |
<title>Interactive Docker Registry</title> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> | |
<script | |
src="https://code.jquery.com/jquery-3.1.1.min.js" | |
integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" | |
crossorigin="anonymous"></script> | |
<script lang="javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> | |
<style> | |
body { margin: 5px 50px; } | |
a.delete { color: red; font-weight: bold; cursor: pointer; } | |
</style> | |
<script lang="javascript"> | |
$(window).on('load', function(){ | |
$('a.delete').on('click', function(e) { | |
e.preventDefault(); | |
var link = $(this); | |
var tag = link.attr('data-tag'); | |
var c = confirm("Are you sure you want to delete tag '" + tag + "' ?"); | |
if(!c) return false; | |
$.ajax({ | |
type: 'DELETE', | |
url: link.attr('href') | |
}).done(function(e) { | |
window.location.reload(); | |
}); | |
return false; | |
}); | |
}); | |
</script> | |
</head><body> | |
<div class="content"> | |
<h1><a href="?registry=<?= $registry ?>">Interactive Docker Registry</a></h1> | |
<?= $delete ?> | |
<?= $digestsLink ?> | |
<pre> | |
<?= $prt($data) ?> | |
</pre> | |
</div> | |
</body></html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment