mirror of https://github.com/MISP/MISP
add: configure gnupg, add gnupg tests
parent
948c99165e
commit
b907a5b9ec
|
@ -14,4 +14,5 @@ docker/run/
|
|||
config.json
|
||||
phpunit.xml
|
||||
docker-compose.override.yml
|
||||
.gnupg
|
||||
.gnupg
|
||||
*.asc
|
|
@ -16,7 +16,7 @@ services:
|
|||
ADMIN_ORG: ${ADMIN_ORG}
|
||||
ADMIN_EMAIL: ${ADMIN_EMAIL}
|
||||
ADMIN_INITIAL_PASSWORD: ${ADMIN_INITIAL_PASSWORD}
|
||||
ADMIN_USER_API_KEY: ${ADMIN_USER_API_KEY}
|
||||
ADMIN_API_KEY: ${ADMIN_API_KEY}
|
||||
GPG_PASSPHRASE: ${GPG_PASSPHRASE}
|
||||
DISABLE_BACKGROUND_WORKERS: ${DISABLE_BACKGROUND_WORKERS:-0}
|
||||
NUM_WORKERS_DEFAULT: ${NUM_WORKERS_DEFAULT:-5}
|
||||
|
|
|
@ -6,18 +6,19 @@ MISP_COMMIT=
|
|||
MODULES_TAG=
|
||||
MODULES_COMMIT=
|
||||
|
||||
MYSQL_ROOT_PASSWORD=root
|
||||
MYSQL_DATABASE=misp3
|
||||
MYSQL_USER=misp
|
||||
MYSQL_PASSWORD=misp
|
||||
|
||||
ADMIN_ORG=
|
||||
ADMIN_ORG_UUID=
|
||||
ADMIN_EMAIL=
|
||||
ADMIN_INITIAL_PASSWORD=
|
||||
ADMIN_USER_API_KEY=
|
||||
ADMIN_API_KEY=
|
||||
GPG_PASSPHRASE=
|
||||
|
||||
MYSQL_ROOT_PASSWORD=root
|
||||
MYSQL_DATABASE=misp3
|
||||
MYSQL_USER=misp
|
||||
MYSQL_PASSWORD=misp
|
||||
|
||||
EMAIL_HOST=mailhog
|
||||
EMAIL_PORT=1025
|
||||
EMAIL_USERNAME=
|
||||
|
|
|
@ -1,23 +1,32 @@
|
|||
# General settings
|
||||
ENV=prod
|
||||
DEBUG=0
|
||||
DOCKER_HUB_PROXY=
|
||||
|
||||
# MISP version
|
||||
MISP_TAG=
|
||||
MISP_COMMIT=
|
||||
MODULES_TAG=
|
||||
MODULES_COMMIT=
|
||||
|
||||
# MISP settings
|
||||
ADMIN_ORG=
|
||||
ADMIN_ORG_UUID=
|
||||
ADMIN_EMAIL=
|
||||
ADMIN_INITIAL_PASSWORD=
|
||||
ADMIN_API_KEY=
|
||||
GPG_PASSPHRASE=
|
||||
# MISP.email, used for notifications. Also used
|
||||
# for GnuPG.email and GPG autogeneration.
|
||||
# MISP_EMAIL=
|
||||
|
||||
# MySQL settings
|
||||
MYSQL_ROOT_PASSWORD=
|
||||
MYSQL_DATABASE=misp3
|
||||
MYSQL_USER=misp
|
||||
MYSQL_PASSWORD=
|
||||
|
||||
ADMIN_ORG=
|
||||
ADMIN_ORG_UUID=
|
||||
ADMIN_EMAIL=
|
||||
ADMIN_INITIAL_PASSWORD=
|
||||
ADMIN_USER_API_KEY=
|
||||
GPG_PASSPHRASE=
|
||||
|
||||
# Email and SMTP settings
|
||||
EMAIL_HOST=
|
||||
EMAIL_PORT=
|
||||
EMAIL_USERNAME=
|
||||
|
|
|
@ -6,18 +6,19 @@ MISP_COMMIT=
|
|||
MODULES_TAG=
|
||||
MODULES_COMMIT=
|
||||
|
||||
MYSQL_ROOT_PASSWORD=root
|
||||
MYSQL_DATABASE=misp3_test
|
||||
MYSQL_USER=misp
|
||||
MYSQL_PASSWORD=misp
|
||||
|
||||
ADMIN_ORG=ORGNAME
|
||||
ADMIN_ORG_UUID=a1f2be0f-73a4-4b4a-9a87-26a8ae0511fb
|
||||
ADMIN_EMAIL=admin@admin.test
|
||||
ADMIN_INITIAL_PASSWORD=admin
|
||||
ADMIN_USER_API_KEY=5E8sFPZa9K6ge1q1INmgkaeVu1mhv6cEg2Hsmx2Y
|
||||
ADMIN_API_KEY=5E8sFPZa9K6ge1q1INmgkaeVu1mhv6cEg2Hsmx2Y
|
||||
GPG_PASSPHRASE=foobar
|
||||
|
||||
MYSQL_ROOT_PASSWORD=root
|
||||
MYSQL_DATABASE=misp3_test
|
||||
MYSQL_USER=misp
|
||||
MYSQL_PASSWORD=misp
|
||||
|
||||
EMAIL_HOST=mailhog
|
||||
EMAIL_PORT=1025
|
||||
EMAIL_USERNAME=
|
||||
|
|
|
@ -79,7 +79,7 @@ RUN pecl install -f xdebug pcov \
|
|||
# Install additional packages
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y \
|
||||
git sendmail \
|
||||
git sendmail sudo \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
|
|
@ -61,5 +61,15 @@ return [
|
|||
'supervisor_port' => '9001',
|
||||
'supervisor_user' => 'supervisor',
|
||||
'supervisor_password' => 'supervisor',
|
||||
],
|
||||
'GnuPG' => [
|
||||
'onlyencrypted' => false,
|
||||
'email' => env('MISP_EMAIL', env('ADMIN_EMAIL')),
|
||||
'homedir' => env('GPG_DIR', '/var/www/.gnupg'),
|
||||
'password' => env('GPG_PASSPHRASE', 'passphrase'),
|
||||
'bodyonlyencrypted' => false,
|
||||
'sign' => true,
|
||||
'obscure_subject' => false,
|
||||
'binary' => '/usr/bin/gpg'
|
||||
]
|
||||
];
|
||||
|
|
|
@ -13,6 +13,11 @@ rm -f "${MISP_READY_STATUS_FLAG}"
|
|||
[ -z "$MISP_DB" ] && MISP_DB=misp3
|
||||
[ -z "$MYSQL_PWD" ] && MYSQL_PWD=$MISP_DB_PASSWORD
|
||||
[ -z "$MYSQLCMD" ] && MYSQLCMD="mysql --defaults-file=/etc/mysql/conf.d/misp.cnf -P $MYSQL_PORT -h $MYSQL_HOST -r -N $MISP_DB"
|
||||
[ -z "$GPG_PASSPHRASE" ] && GPG_PASSPHRASE="passphrase"
|
||||
[ -z "$GPG_DIR" ] && GPG_DIR="/var/www/.gnupg"
|
||||
|
||||
# Switches to selectively disable configuration logic
|
||||
[ -z "$AUTOCONF_GPG" ] && AUTOCONF_GPG="true"
|
||||
|
||||
# create mysql default config
|
||||
cat <<EOF >/etc/mysql/conf.d/misp.cnf
|
||||
|
@ -70,12 +75,55 @@ init_user() {
|
|||
ADMIN_USER_ID=$(echo "SELECT id FROM users WHERE EMAIL='${ADMIN_EMAIL}';" | ${MYSQLCMD} | tr -d '\n')
|
||||
|
||||
# Insert Admin user API key
|
||||
if [ -z "$ADMIN_USER_API_KEY" ]; then
|
||||
if [ -z "$ADMIN_API_KEY" ]; then
|
||||
echo >&2 "Creating admin user API key..."
|
||||
ADMIN_USER_API_KEY_START=$(echo ${ADMIN_USER_API_KEY} | head -c 4)
|
||||
ADMIN_USER_API_KEY_END=$(echo ${ADMIN_USER_API_KEY} | tail -c 5)
|
||||
export ADMIN_USER_API_KEY_HASH=$(php -r "echo password_hash('${ADMIN_USER_API_KEY}', PASSWORD_DEFAULT);" | tr -d \')
|
||||
echo "INSERT INTO auth_keys (uuid, authkey, authkey_start, authkey_end, created, expiration, user_id) VALUES ((SELECT uuid()), '${ADMIN_USER_API_KEY_HASH}', '${ADMIN_USER_API_KEY_START}', '${ADMIN_USER_API_KEY_END}', 0, 0, ${ADMIN_USER_ID});" | ${MYSQLCMD}
|
||||
ADMIN_API_KEY_START=$(echo ${ADMIN_API_KEY} | head -c 4)
|
||||
ADMIN_API_KEY_END=$(echo ${ADMIN_API_KEY} | tail -c 5)
|
||||
export ADMIN_API_KEY_HASH=$(php -r "echo password_hash('${ADMIN_API_KEY}', PASSWORD_DEFAULT);" | tr -d \')
|
||||
echo "INSERT INTO auth_keys (uuid, authkey, authkey_start, authkey_end, created, expiration, user_id) VALUES ((SELECT uuid()), '${ADMIN_API_KEY_HASH}', '${ADMIN_API_KEY_START}', '${ADMIN_API_KEY_END}', 0, 0, ${ADMIN_USER_ID});" | ${MYSQLCMD}
|
||||
fi
|
||||
}
|
||||
|
||||
configure_gnupg() {
|
||||
if [ "$AUTOCONF_GPG" != "true" ]; then
|
||||
echo "... GPG auto configuration disabled"
|
||||
return
|
||||
fi
|
||||
|
||||
GPG_DIR=/var/www/.gnupg
|
||||
GPG_ASC=/var/www/html/webroot/gpg.asc
|
||||
GPG_TMP=/tmp/gpg.tmp
|
||||
|
||||
if [ ! -f "${GPG_DIR}/trustdb.gpg" ]; then
|
||||
echo "... generating new GPG key in ${GPG_DIR}"
|
||||
cat >${GPG_TMP} <<GPGEOF
|
||||
%echo Generating a basic OpenPGP key
|
||||
Key-Type: RSA
|
||||
Key-Length: 3072
|
||||
Name-Real: MISP Admin
|
||||
Name-Email: ${MISP_EMAIL-$ADMIN_EMAIL}
|
||||
Expire-Date: 0
|
||||
Passphrase: $GPG_PASSPHRASE
|
||||
%commit
|
||||
%echo Done
|
||||
GPGEOF
|
||||
mkdir -p ${GPG_DIR}
|
||||
gpg --homedir ${GPG_DIR} --gen-key --batch ${GPG_TMP}
|
||||
rm -f ${GPG_TMP}
|
||||
else
|
||||
echo "... found pre-generated GPG key in ${GPG_DIR}"
|
||||
fi
|
||||
|
||||
# Fix permissions
|
||||
chown -R www-data:www-data ${GPG_DIR}
|
||||
find ${GPG_DIR} -type f -exec chmod 600 {} \;
|
||||
find ${GPG_DIR} -type d -exec chmod 700 {} \;
|
||||
|
||||
if [ ! -f ${GPG_ASC} ]; then
|
||||
echo "... exporting GPG key"
|
||||
sudo -u www-data gpg --homedir ${GPG_DIR} --export --armor ${MISP_EMAIL-$ADMIN_EMAIL} >${GPG_ASC}
|
||||
else
|
||||
echo "... found exported key ${GPG_ASC}"
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -91,6 +139,8 @@ done
|
|||
|
||||
init_user
|
||||
|
||||
configure_gnupg
|
||||
|
||||
# Test php-fpm config
|
||||
php-fpm -t
|
||||
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
namespace App\Lib\Tools;
|
||||
|
||||
use Cake\Core\Exception\Exception;
|
||||
use Cake\Core\Configure;
|
||||
use Exception;
|
||||
|
||||
class CryptGpgExtended extends \Crypt_GPG
|
||||
{
|
||||
|
@ -64,7 +63,7 @@ class CryptGpgExtended extends \Crypt_GPG
|
|||
$operation = '--export';
|
||||
$operation .= ' ' . escapeshellarg($fingerprint);
|
||||
|
||||
$arguments = array('--export-options', 'export-minimal');
|
||||
$arguments = ['--export-options', 'export-minimal'];
|
||||
if ($armor) {
|
||||
$arguments[] = '--armor';
|
||||
}
|
||||
|
|
|
@ -0,0 +1,258 @@
|
|||
<?php
|
||||
|
||||
namespace App\Lib\Tools;
|
||||
|
||||
use Exception;
|
||||
use Generator;
|
||||
|
||||
class TmpFileTool
|
||||
{
|
||||
/** @var resource|null */
|
||||
private $tmpfile;
|
||||
|
||||
/** @var string */
|
||||
private $separator;
|
||||
|
||||
/**
|
||||
* @param int $maxInMemory How many bytes should keep in memory before creating file on disk. By default is is 5 MB.
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct($maxInMemory = null)
|
||||
{
|
||||
if ($maxInMemory === null) {
|
||||
$maxInMemory = 5 * 1024 * 1024;
|
||||
}
|
||||
$this->tmpfile = fopen("php://temp/maxmemory:$maxInMemory", "w+");
|
||||
if ($this->tmpfile === false) {
|
||||
throw new Exception('Could not create temporary file.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data to stream with separator. Separator will be prepend to content for next call.
|
||||
* @param string|Generator $content
|
||||
* @param string $separator
|
||||
* @throws Exception
|
||||
*/
|
||||
public function writeWithSeparator($content, $separator)
|
||||
{
|
||||
if (isset($this->separator)) {
|
||||
if ($content instanceof Generator) {
|
||||
$this->write($this->separator);
|
||||
foreach ($content as $part) {
|
||||
$this->write($part);
|
||||
}
|
||||
} else {
|
||||
$this->write($this->separator . $content);
|
||||
}
|
||||
} else {
|
||||
if ($content instanceof Generator) {
|
||||
foreach ($content as $part) {
|
||||
$this->write($part);
|
||||
}
|
||||
} else {
|
||||
$this->write($content);
|
||||
}
|
||||
}
|
||||
$this->separator = $separator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $content
|
||||
* @throws Exception
|
||||
*/
|
||||
public function write($content)
|
||||
{
|
||||
if (fwrite($this->tmpfile, $content) === false) {
|
||||
if ($this->tmpfile === null) {
|
||||
throw new Exception('Could not write to finished temporary file.');
|
||||
}
|
||||
$tmpFolder = sys_get_temp_dir();
|
||||
$freeSpace = disk_free_space($tmpFolder);
|
||||
throw new Exception("Could not write to temporary file in $tmpFolder folder. Maybe not enough space? ($freeSpace bytes left)");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @throws Exception
|
||||
*/
|
||||
public function writeFromFile($path)
|
||||
{
|
||||
$file = fopen($path, 'r');
|
||||
if (!$file) {
|
||||
throw new Exception("Could not open file $file.");
|
||||
}
|
||||
if (stream_copy_to_stream($file, $this->tmpfile) === false) {
|
||||
throw new Exception("Could not copy content of file $file into TmpFile.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns generator of parsed CSV line from file.
|
||||
*
|
||||
* @param string $delimiter
|
||||
* @param string $enclosure
|
||||
* @param string $escape
|
||||
* @return Generator<array>
|
||||
* @throws Exception
|
||||
*/
|
||||
public function intoParsedCsv($delimiter = ',', $enclosure = '"', $escape = "\\")
|
||||
{
|
||||
$this->rewind();
|
||||
$line = 0;
|
||||
while (!feof($this->tmpfile)) {
|
||||
$result = fgetcsv($this->tmpfile, 0, $delimiter, $enclosure, $escape);
|
||||
if ($result === false) {
|
||||
throw new Exception("Could not read line $line from temporary CSV file.");
|
||||
}
|
||||
$line++;
|
||||
yield $result;
|
||||
}
|
||||
$this->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns generator of line from file.
|
||||
*
|
||||
* @return Generator
|
||||
* @throws Exception
|
||||
*/
|
||||
public function intoLines()
|
||||
{
|
||||
$this->rewind();
|
||||
while (!feof($this->tmpfile)) {
|
||||
$result = fgets($this->tmpfile);
|
||||
if ($result === false) {
|
||||
throw new Exception('Could not read line from temporary file.');
|
||||
}
|
||||
yield $result;
|
||||
}
|
||||
$this->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $chunkSize In bytes
|
||||
* @return Generator
|
||||
* @throws Exception
|
||||
*/
|
||||
public function intoChunks($chunkSize = 8192)
|
||||
{
|
||||
$this->rewind();
|
||||
while (!feof($this->tmpfile)) {
|
||||
$result = fread($this->tmpfile, $chunkSize);
|
||||
if ($result === false) {
|
||||
throw new Exception('Could not read from temporary file.');
|
||||
}
|
||||
yield $result;
|
||||
}
|
||||
$this->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function intoString()
|
||||
{
|
||||
$this->rewind();
|
||||
$string = stream_get_contents($this->tmpfile);
|
||||
if ($string === false) {
|
||||
throw new Exception('Could not read from temporary file.');
|
||||
}
|
||||
$this->close();
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass data to output.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function intoOutput()
|
||||
{
|
||||
$this->rewind();
|
||||
if (fpassthru($this->tmpfile) === false) {
|
||||
throw new Exception('Could not pass temporary file to output.');
|
||||
}
|
||||
$this->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return resource
|
||||
* @throws Exception
|
||||
*/
|
||||
public function resource()
|
||||
{
|
||||
$this->rewind();
|
||||
return $this->tmpfile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* @throws Exception
|
||||
*/
|
||||
public function size()
|
||||
{
|
||||
$this->isOpen();
|
||||
return fstat($this->tmpfile)['size'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $algo
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function hash($algo)
|
||||
{
|
||||
$this->rewind();
|
||||
$hash = hash_init($algo);
|
||||
hash_update_stream($hash, $this->tmpfile);
|
||||
return hash_final($hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->intoString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
if ($this->tmpfile) {
|
||||
$result = fclose($this->tmpfile);
|
||||
$this->tmpfile = null;
|
||||
return $result;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
private function isOpen()
|
||||
{
|
||||
if ($this->tmpfile === null) {
|
||||
throw new Exception('Temporary file is already closed.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek to start of file.
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
private function rewind()
|
||||
{
|
||||
$this->isOpen();
|
||||
if (fseek($this->tmpfile, 0) === -1) {
|
||||
throw new Exception('Could not seek to start of temporary file.');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace App\Test\TestCase\Tool;
|
||||
|
||||
use App\Lib\Tools\CryptGpgExtended;
|
||||
use App\Lib\Tools\TmpFileTool;
|
||||
use Cake\TestSuite\TestCase;
|
||||
use Cake\Core\Configure;
|
||||
|
||||
// use PHPUnit\Framework\TestCase;
|
||||
|
||||
class CryptGpgExtendedTest extends TestCase
|
||||
{
|
||||
public function testInit(): void
|
||||
{
|
||||
$gpg = $this->init();
|
||||
$this->assertInstanceOf('App\Lib\Tools\CryptGpgExtended', $gpg);
|
||||
$this->assertIsString($gpg->getVersion());
|
||||
}
|
||||
|
||||
public function testSignAndVerify()
|
||||
{
|
||||
$gpg = $this->init();
|
||||
$config = Configure::read('GnuPG');
|
||||
|
||||
$gpg->addSignKey($config['email'], $config['password']);
|
||||
|
||||
$testString = 'ahojSvete';
|
||||
|
||||
$signature = $gpg->sign($testString, \Crypt_GPG::SIGN_MODE_DETACHED, \Crypt_GPG::ARMOR_BINARY);
|
||||
$this->assertIsString($signature);
|
||||
|
||||
$verified = $gpg->verify($testString, $signature);
|
||||
$this->assertIsArray($verified);
|
||||
$this->assertCount(1, $verified);
|
||||
$this->assertTrue($verified[0]->isValid());
|
||||
|
||||
$signature = $gpg->sign($testString, \Crypt_GPG::SIGN_MODE_DETACHED, \Crypt_GPG::ARMOR_ASCII);
|
||||
$this->assertIsString($signature);
|
||||
|
||||
$verified = $gpg->verify($testString, $signature);
|
||||
$this->assertIsArray($verified);
|
||||
$this->assertCount(1, $verified);
|
||||
$this->assertTrue($verified[0]->isValid());
|
||||
|
||||
// Tmp file
|
||||
$tmpFile = new TmpFileTool();
|
||||
$tmpFile->write($testString);
|
||||
$signature = $gpg->signFile($tmpFile, null, \Crypt_GPG::SIGN_MODE_DETACHED, \Crypt_GPG::ARMOR_BINARY);
|
||||
$this->assertIsString($signature);
|
||||
|
||||
$verified = $gpg->verify($testString, $signature);
|
||||
$this->assertIsArray($verified);
|
||||
$this->assertCount(1, $verified);
|
||||
$this->assertTrue($verified[0]->isValid());
|
||||
}
|
||||
|
||||
private function init(): CryptGpgExtended
|
||||
{
|
||||
$config = Configure::read('GnuPG');
|
||||
|
||||
$options = [
|
||||
'homedir' => $config['homedir'],
|
||||
'gpgconf' => $config['gpgconf'] ?? null,
|
||||
'binary' => $config['binary'] ?? '/usr/bin/gpg',
|
||||
];
|
||||
|
||||
return new CryptGpgExtended($options);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue