Skip to content

Commit 347b6e6

Browse files
author
philip
committed
init
0 parents  commit 347b6e6

16 files changed

Lines changed: 1495 additions & 0 deletions

README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
##PHP docker registry 2 token authentication
2+
3+
This is a light weight docker token authentication build in php to the specs of the (docs)[https://docs.docker.com/registry/spec/auth/token/].
4+
5+
It can be used to validate push/pull and registration us users for you private registry.
6+
7+
###Configuring
8+
9+
prop.public_key the private key, should be the content not file location
10+
prop.private_key the public key, should be the content not file location
11+
prop.audience audience that is registered in the registry (server name of registry)
12+
prop.issuer issuer that is registered in the registry (server name of this server)
13+
prop.log_level array of log levels to display
14+
prop.log_file log file for logging , if non is given it will use stdout
15+
16+
the config should be given as argument with the constructor:
17+
18+
```
19+
$app = new DockerToken\Application([
20+
'prop.public_key' => file_get_contents(dirname(__FILE__) . '/public.key'),
21+
'prop.private_key' => file_get_contents(dirname(__FILE__) . '/private.key'),
22+
'prop.audience' => 'registry.docker.com',
23+
'prop.issuer' => 'auth.docker.com',
24+
])
25+
```
26+
27+
###Validating
28+
29+
This application has on it self no validation, this can be added by adding a listener.(see example.php)
30+
31+
If credentials are invalid you can throw a InvalidAccessException this will result in a 401 response.
32+
33+
###Running
34+
35+
php -S 127.0.0.1:9999 example.php
36+
37+
###Keys
38+
39+
to generate keys, see:
40+
41+
```
42+
php bin/CreateKeys.php
43+
```

bin/CreateKeys.php

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
<?php
2+
/**
3+
* @author Philip Bergman <pbergman@live.nl>
4+
* @copyright Philip Bergman
5+
*/
6+
7+
class CreateKeysCommand
8+
{
9+
protected $input;
10+
11+
function __construct()
12+
{
13+
$this->input = array_merge(
14+
$this->defaults(),
15+
$this->processOptions()
16+
);
17+
$this->validateSetOptions();
18+
$this->validateNotSetOptions();
19+
if (isset($this->input['help'])) {
20+
echo $this->getHelper();
21+
exit();
22+
}
23+
24+
}
25+
26+
public function run()
27+
{
28+
if (strrev($this->input['folder']) !== DIRECTORY_SEPARATOR) {
29+
$this->input['folder'] .= DIRECTORY_SEPARATOR;
30+
}
31+
$files = [];
32+
foreach (['pub', 'key', 'crt', 'csr'] as $extension) {
33+
$files[$extension] = sprintf(
34+
'%s%s%s.%s',
35+
$this->input['folder'],
36+
$this->input['prefix'],
37+
$this->input['hostname'],
38+
$extension
39+
);
40+
}
41+
foreach ($files as $file) {
42+
if (file_exists($file)) {
43+
throw new RuntimeException(sprintf('File exist: %s', $file));
44+
}
45+
}
46+
$dn = array(
47+
"countryName" => $this->input['country'],
48+
"stateOrProvinceName" => $this->input['state-or-province-name'],
49+
"localityName" => $this->input['locality-name'],
50+
"organizationName" => $this->input['organization-name'],
51+
"organizationalUnitName" => $this->input['organizational-unit-name'],
52+
"commonName" => $this->input['common-name'],
53+
"emailAddress" => $this->input['email-address'],
54+
);
55+
// Create the private and public key
56+
$res = openssl_pkey_new([
57+
'digest_alg' => $this->input['alg'],
58+
'private_key_bits' => $this->input['bits'],
59+
'private_key_type' => OPENSSL_KEYTYPE_RSA,
60+
]);
61+
// Generate a certificate signing request
62+
$csr = openssl_csr_new($dn, $res);
63+
// Creates a self-signed cert
64+
$sscert = openssl_csr_sign($csr, null, $res, $this->input['days']);
65+
openssl_csr_export($csr, $out);
66+
file_put_contents($files['csr'], $out);
67+
// Export certfile
68+
openssl_x509_export($sscert, $out);
69+
file_put_contents($files['crt'], $out);
70+
// Extract the private key from $res to $privKey
71+
openssl_pkey_export($res, $out);
72+
file_put_contents($files['key'], $out);
73+
// Extract the public key from $res to $pubKey
74+
$out = openssl_pkey_get_details($res);
75+
file_put_contents($files['pub'], $out["key"]);
76+
77+
}
78+
79+
protected function validateSetOptions()
80+
{
81+
foreach($this->input as $name => $value) {
82+
switch ($name) {
83+
case 'folder':
84+
if (!is_dir($value) && false === mkdir($value)) {
85+
throw new InvalidArgumentException(sprintf('Could not create folder: "%s"', $value));
86+
}
87+
break;
88+
}
89+
}
90+
}
91+
92+
protected function validateNotSetOptions()
93+
{
94+
$options = array_values(array_map(
95+
function($v) {
96+
return $this->getRawName($v);
97+
},
98+
$this->getOptions()
99+
));
100+
101+
$options = array_filter(
102+
$options,
103+
function($v) {
104+
return !array_key_exists($v, $this->input);
105+
}
106+
);
107+
108+
foreach ($options as $option) {
109+
switch($option) {
110+
case 'country':
111+
case 'state-or-province-name':
112+
case 'locality-name':
113+
case 'organization-name':
114+
case 'organizational-unit-name':
115+
case 'common-name':
116+
case 'email-address':
117+
$name = strtr(sprintf('%s: ', $option), '-', ' ');
118+
$this->input[$option] = $this->readline($name);
119+
}
120+
}
121+
}
122+
123+
protected function readline($text)
124+
{
125+
if (function_exists('readline')) {
126+
return readline($text);
127+
} else {
128+
fwrite(STDOUT, $text);
129+
return trim(fgets(STDIN));
130+
}
131+
}
132+
133+
protected function defaults()
134+
{
135+
return [
136+
'bits' => 2048,
137+
'alg' => 'sha512',
138+
'folder' => getcwd(),
139+
'days' => 356,
140+
'prefix' => null,
141+
'hostname' => gethostname(),
142+
];
143+
}
144+
145+
public function processOptions()
146+
{
147+
$options = $this->getOptions();
148+
$return = [];
149+
$input = getopt(implode('', array_keys($options)), array_values($options));
150+
151+
foreach ($options as $short => $long) {
152+
if (isset($input[$this->getRawName($short)])) {
153+
$return[$this->getRawName($long)] = $input[$this->getRawName($short)];
154+
}
155+
if (isset($input[$this->getRawName($long)])) {
156+
$return[$this->getRawName($long)] = $input[$this->getRawName($long)];
157+
}
158+
}
159+
return $return;
160+
}
161+
162+
protected function getRawName($name)
163+
{
164+
while (strrev($name)[0] === ':') {
165+
$name = substr($name, 0, -1);
166+
}
167+
168+
return $name;
169+
}
170+
171+
public function getOptions()
172+
{
173+
return [
174+
'f:' => 'folder:',
175+
'p:' => 'prefix:',
176+
'c:' => 'country:',
177+
's:' => 'state-or-province-name:',
178+
'l:' => 'locality-name:',
179+
'o:' => 'organization-name:',
180+
'u:' => 'organizational-unit-name:',
181+
'n:' => 'common-name:',
182+
'e:' => 'email-address:',
183+
'h:' => 'hostname:',
184+
185+
];
186+
}
187+
188+
public function getHelper()
189+
{
190+
$cwd = getcwd();
191+
192+
return <<<EOH
193+
194+
Available options:
195+
196+
-b, --bits Private key bits (default 2048)
197+
-a, --alg Digest alg (sha512)
198+
-f, --folder The folder where to save the keys (default: "$cwd")
199+
-p, --prefix Prefix for the keys
200+
-d, --days Days csr valid (365)
201+
-c, --country Country used for csr
202+
-s, --state-or-province-name State or province name used for csr
203+
-l, --locality-name Locality name used for csr
204+
-o, --organization-name Organization name used for csr
205+
-u, --organizational-unit-name Organizational unit name used for csr
206+
-n, --common-name Common name used for csr
207+
-e, --email-address Email address used for csr
208+
209+
EOH;
210+
211+
}
212+
213+
}
214+
(new CreateKeysCommand())->run();exit;

composer.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
3+
"name": "silex/silex",
4+
"description": "The PHP micro-framework based on the Symfony2 Components",
5+
"keywords": ["docker", "docker authentication", "docker token", "token"],
6+
"license": "MIT",
7+
"authors": [
8+
{
9+
"name": "Philip Bergman",
10+
"email": "pbergman@live.nl"
11+
}
12+
],
13+
"autoload": {
14+
"psr-4": { "DockerToken\\": "src/DockerToken" }
15+
},
16+
"require": {
17+
"php": ">=5.4.0",
18+
"silex/silex": "^1.3",
19+
"firebase/php-jwt": "^2.2",
20+
"christian-riesen/base32": "~1.2"
21+
}
22+
}

0 commit comments

Comments
 (0)