Skip to content

Commit

Permalink
Merge branch 'release/v2.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
mettke committed Jun 25, 2019
2 parents 559a2f2 + 7242c40 commit a06f44a
Show file tree
Hide file tree
Showing 18 changed files with 303 additions and 37 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Features
* All access changes are logged to the database and to the system logs. Granting of access is also reported by email.
* Be notified when a server becomes orphaned (has no active administrators).
* Introduce key depreciation to encouraging users to replace their public keys
* Support for encrypted SSH Keys (does not work with the openssh format. Instead use a different one like `ssh-keygen -m PEM`)

Demo
----
Expand Down
1 change: 1 addition & 0 deletions config/config-sample.ini
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ logo = /logo-header-itmettke.png
; footer may contain HTML. Literal & " < and > should be escaped as &amp;
; &quot; &lt; $gt;
footer = 'Developed by <a href="https://github.com/mettke/ssh-key-authority">Marc Mettke</a>'
key_password_enabled = 0

[general]
; Use timeout --version to find out the current version
Expand Down
14 changes: 14 additions & 0 deletions core.php
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,20 @@ function simplify_search($defaults, $values) {
}
}

function unsaved_changes_exist() {
global $sync_request_dir;
return $sync_request_dir->count_pending_sync_requests() > 0;
}

function get_unlocked_timer() {
$file = fopen("/var/run/keys/keys-sync.timer", "r");
if(!$file) return 0;
$timer = fread($file, 4);
if(!$timer) return 0;
fclose($file);
return (int) $timer;
}

class OutputFormatter {
public function comment_format($text) {
return hesc($text);
Expand Down
9 changes: 6 additions & 3 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ FROM alpine
LABEL maintainer="Marc Mettke <[email protected]>"

ENV SYSTEM https://github.com/mettke/ssh-key-authority.git
ENV TAG v2.0.5
ENV TAG v2.1.0
ADD entrypoint.sh /entrypoint.sh
ADD healthcheck.sh /healthcheck.sh
ADD cron /var/spool/cron/crontabs/root

RUN mkdir -p /var/log/keys/ /run/php/ /ska/ && \
RUN mkdir -p /var/log/keys/ /var/run/keys /run/php/ /ska/ && \
adduser --system --disabled-password keys-sync && \
apk add openssh \
php7 \
Expand All @@ -18,16 +18,19 @@ RUN mkdir -p /var/log/keys/ /run/php/ /ska/ && \
php7-mysqli \
php7-pcntl \
php7-posix \
php7-xml \
rsync \
ssmtp \
sudo && \
sed -i -e '/listen =/ s/= .*/= 9000/' /etc/php7/php-fpm.d/www.conf && \
sed -i -e '/user =/ s/.*/user = keys-sync/' /etc/php7/php-fpm.d/www.conf && \
sed -i -e '/;pid =/ s/.*/pid = \/var\/run\/php-fpm.pid/' /etc/php7/php-fpm.conf && \
chmod +x /entrypoint.sh /healthcheck.sh && \
chown keys-sync:nogroup /var/run/keys && \
ln -sf /dev/stderr /var/log/php7/error.log
RUN apk add git && \
git clone ${SYSTEM} /ska && \
git -C /ska checkout ${TAG} && \
git -C /ska checkout ${TAG} && \
apk del git && \
chown -R keys-sync:nogroup /ska/config

Expand Down
2 changes: 1 addition & 1 deletion docker/cron
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
0 1 * * * /ska/scripts/cron.php
*/1 * * * * /bin/ash -c "PID=$(cat /var/run/keys-sync.pid) && [ -n ${PID} -a -d /proc/${PID} ] || /ska/scripts/syncd.php --user keys-sync"
*/1 * * * * /bin/ash -c "PID=$(cat /var/run/keys/keys-sync.pid) && [ -n ${PID} -a -d /proc/${PID} ] || /ska/scripts/syncd.php --user keys-sync"
2 changes: 1 addition & 1 deletion docker/healthcheck.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env ash
for PID_FILE in /var/run/crond.pid /var/run/keys-sync.pid /var/run/php-fpm.pid; do
for PID_FILE in /var/run/crond.pid /var/run/keys/keys-sync.pid /var/run/php-fpm.pid; do
PID=$(cat ${PID_FILE})
if ! [ -n "${PID}" -a -d "/proc/${PID}" ]; then
exit 1
Expand Down
1 change: 1 addition & 0 deletions examples/shared/config-ldap/config.ini
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ logo = /logo-header-itmettke.png
; footer may contain HTML. Literal & " < and > should be escaped as &amp;
; &quot; &lt; $gt;
footer = 'Developed by <a href="https://github.com/mettke/ssh-key-authority">Marc Mettke</a>'
key_password_enabled = 0

[general]
; Use timeout --version to find out the current version
Expand Down
1 change: 1 addition & 0 deletions examples/shared/config-local/config.ini
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ logo = /logo-header-itmettke.png
; footer may contain HTML. Literal & " < and > should be escaped as &amp;
; &quot; &lt; $gt;
footer = 'Developed by <a href="https://github.com/mettke/ssh-key-authority">Marc Mettke</a>'
key_password_enabled = 0

[general]
; Use timeout --version to find out the current version
Expand Down
15 changes: 15 additions & 0 deletions model/syncrequestdirectory.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,21 @@ public function list_pending_sync_requests() {
}
return $reqs;
}

/**
* List the sync requests stored in the database that are not being processed yet.
* @return array of SyncRequest objects
*/
public function count_pending_sync_requests() {
$this->sync_list_stmt = $this->database->prepare("SELECT COUNT(*) as total FROM sync_request WHERE processing = 0 ORDER BY id");
$this->sync_list_stmt->execute();
$result = $this->sync_list_stmt->get_result();
$reqs = 0;
while($row = $result->fetch_assoc()) {
$reqs = $row['total'];
}
return $reqs;
}
}

class SyncRequestNotFoundException extends Exception {}
30 changes: 30 additions & 0 deletions public_html/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,36 @@ a {
}
a:focus, a:hover {
color: #611d42;
}

#changes {
top: 0;
float: right;
border: 1px solid;
padding: 10px;
margin-top: 19px !important;
box-shadow: 2px 2px 2px #ddd;
border-color: #ddd;
}
#changes > a {
color: #000;
text-decoration: none;
}
#changes > button {
padding: 0;
border: none;
background: none;
}
#changes-icon-warn {
color: gold;
}
#changes-icon-succ {
color: gold;
}
.help {
padding-top: 10px;
padding-bottom: 10px;
}

@media (min-width: 768px) and (max-width: 991px) {
#wrap > .container {
Expand Down
38 changes: 30 additions & 8 deletions requesthandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,38 @@
}
$router->handle_request($relative_request_url);
if(isset($router->view)) {
$view = path_join($base_path, 'views', $router->view.'.php');
if(file_exists($view)) {
if($active_user->auth_realm == 'LDAP' || $active_user->auth_realm == 'local' || $router->public) {
require($view);
if(isset($_POST['password_entry']) && !isset($_POST['password'])) {
$page = new PageSection('base');
$page->set('title', 'Unlock Key');
$page->set('content', new PageSection('password_entry'));
$page->set('alerts', $active_user->pop_alerts());
echo $page->generate();
} else {
if(isset($_POST['password_entry']) && isset($_POST['password'])) {
try {
if(file_exists("/var/run/keys/keys-sync.pipe")) {
$file = fopen("/var/run/keys/keys-sync.pipe", "a");
if($file) {
error_log($_POST['password']."\n");
$timer = fwrite($file, $_POST['password']."\n");
fclose($file);
}
}
} catch(ErrorException $e) {
error_log($e);
}
}
$view = path_join($base_path, 'views', $router->view.'.php');
if(file_exists($view)) {
if($active_user->auth_realm == 'LDAP' || $active_user->auth_realm == 'local' || $router->public) {
require($view);
} else {
require('views/error403.php');
die;
}
} else {
require('views/error403.php');
die;
throw new Exception("View file $view missing.");
}
} else {
throw new Exception("View file $view missing.");
}
}

Expand Down
5 changes: 4 additions & 1 deletion scripts/sync-common.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class SyncProcess {
* @param array $args arguments
* @param Request $request object that triggered this sync
*/
public function __construct($command, $args, $request = null) {
public function __construct($command, $args, $password = null, $request = null) {
global $config;
$timeout_util = $config['general']['timeout_util'];

Expand All @@ -38,6 +38,9 @@ public function __construct($command, $args, $request = null) {
$this->handle = proc_open($commandline, $descriptorspec, $this->pipes);
stream_set_blocking($this->pipes[1], 0);
stream_set_blocking($this->pipes[2], 0);
if(!is_null($password)) {
fwrite($this->pipes[0], $password."\n");
}
}

/**
Expand Down
85 changes: 78 additions & 7 deletions scripts/sync.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
}

// Parse the command-line arguments
$options = getopt('h:i:au:p', array('help', 'host:', 'id:', 'all', 'user:', 'preview'));
$options = getopt('h:i:au:pn', array('help', 'host:', 'id:', 'all', 'user:', 'preview', 'no-password'));
if(isset($options['help'])) {
show_help();
exit(0);
Expand All @@ -24,7 +24,8 @@
'i' => 'id',
'a' => 'all',
'u' => 'user',
'p' => 'preview'
'p' => 'preview',
'n' => 'no-password'
);
foreach($short_to_long as $short => $long) {
if(isset($options[$short]) && isset($options[$long])) {
Expand All @@ -49,6 +50,7 @@
$username = null;
}
$preview = isset($options['preview']);
$no_password = isset($options['no-password']);

// Use 'keys-sync' user as the active user (create if it does not yet exist)
try {
Expand Down Expand Up @@ -82,7 +84,7 @@
}
}
} elseif(isset($options['id'])) {
sync_server($options['id'], $username, $preview);
sync_server($options['id'], $username, $preview, $no_password);
exit(0);
}

Expand All @@ -94,6 +96,38 @@
$pending_syncs[$server->hostname] = $server;
}

$password = "";
if(!$no_password) {
$key = new Crypt_RSA();
try {
$key_content = file_get_contents('config/keys-sync');
} catch(ErrorException $e) {
echo date('c')." Unable to load keyfile\n";
exit(1);
}

$success = false;
try {
$success = $key->loadKey($key_content);
} catch(ErrorException $e) {}
if(!$success) {
if(!$no_password) {
try {
echo "Enter Key Password: \n";
system('stty -echo 2> /dev/null');
$password = rtrim(fgets(STDIN), "\n\r\0");
system('stty echo 2> /dev/null');
$key->setPassword($password);
$success = $key->loadKey($key_content);
} catch(ErrorException $e) {}
}
if(!$success) {
echo date('c')." Invalid Key or Password\n";
exit(1);
}
}
}

$sync_procs = array();
define('MAX_PROCS', 20);
while(count($sync_procs) > 0 || count($pending_syncs) > 0) {
Expand All @@ -110,7 +144,10 @@
if($preview) {
$args[] = '--preview';
}
$sync_procs[] = new SyncProcess(__FILE__, $args);
if($no_password) {
$args[] = '--no-password';
}
$sync_procs[] = new SyncProcess(__FILE__, $args, $password);
unset($pending_syncs[$hostname]);
}
foreach($sync_procs as $ref => $sync_proc) {
Expand All @@ -137,11 +174,12 @@ function show_help() {
-u, --user sync only the specified user account
-p, --preview perform no changes, display content of all
keyfiles
-n, --no-password fail instead of prompting for password
--help display this help and exit
<?php
}

function sync_server($id, $only_username = null, $preview = false) {
function sync_server($id, $only_username = null, $preview = false, $no_password = false) {
global $config;
global $server_dir;
global $user_dir;
Expand Down Expand Up @@ -235,6 +273,41 @@ function sync_server($id, $only_username = null, $preview = false) {
return;
}

$key = new Crypt_RSA();
try {
$key_content = file_get_contents('config/keys-sync');
} catch(ErrorException $e) {
echo date('c')." {$hostname}: Public key authentication failed. Unable to load keyfile\n";
$server->sync_report('sync failure', 'SSH authentication failed: Unable to load keyfile');
$server->delete_all_sync_requests();
report_all_accounts_failed($keyfiles);
return;
}

$success = false;
try {
$success = $key->loadKey($key_content);
} catch(ErrorException $e) {}
if(!$success) {
if(!$no_password) {
try {
echo "Enter Key Password: \n";
system('stty -echo 2> /dev/null');
$password = rtrim(fgets(STDIN), "\n\r\0");
system('stty echo 2> /dev/null');
$key->setPassword($password);
$success = $key->loadKey($key_content);
} catch(ErrorException $e) {}
}
if(!$success) {
echo date('c')." {$hostname}: Public key authentication failed. Invalid Key or Password\n";
$server->sync_report('sync failure', 'SSH authentication failed: Invalid Key or Password');
$server->delete_all_sync_requests();
report_all_accounts_failed($keyfiles);
return;
}
}

echo date('c')." {$hostname}: Attempting to connect.\n";
$legacy = false;
$attempts = array('keys-sync', 'root');
Expand Down Expand Up @@ -287,8 +360,6 @@ function sync_server($id, $only_username = null, $preview = false) {
}
}
try {
$key = new Crypt_RSA();
$key->loadKey(file_get_contents('config/keys-sync'));
if ($ssh->login($attempt, $key)) {
echo date('c')." {$hostname}: Logged in as $attempt.\n";
break;
Expand Down
Loading

0 comments on commit a06f44a

Please sign in to comment.