OpenXPKI¶
An open, enterprise-grade PKI/Trustcenter
Table of Contents:¶
Introduction¶
This manual describes the installation and use of the OpenXPKI software, an Open Source trustcenter solution written by The OpenXPKI Project.
The intended audience are CA administrators and operators. We assume that readers are familiar working on a Unix shell and have enough background knowledge about Public Key Infrastructures to understand the relevant terms.
The OpenXPKI manual is split into the following sections:
- The introduction, which you are reading right now. Following this abstract, you will learn more about where to get the software, where to get help. Furthermore, a high-level overview of the system design and some key concepts will be presented.
- The Quickstart guide lays the emphasis on getting a minimal Certificate Authority (CA) without any bells and whistles running. Reference to further configuration options is provided inline, so that you know where to look if you want to configure more advanced features. Please note that setting up a working CA is a complex task and thus the ‘’quick’’ in “quick start” may be a bit euphemistic.
- The setup chapter gives a more detailed introduction with brief configuration examples to adjust the system to your needs based on the existing, pre-defined workflows and code.
- In the operation chapter we describe commands and tools which are required for setting up the non-config items (e.g., the crypto tokens) and to perform daily operation.
- If you need to extend the system, read the developer section to learn how to use the workflow engine and the connector layer to make the system fit your needs.
Key features¶
Assuming this is your first contact with OpenXPKI here is a quick summary of what it is and what it is capable of.
OpenXPKI aims to be an enterprise-scale Public Key Infrastructure (PKI) solution, supporting well established infrastructure components like RDBMS and Hardware Security Modules (HSMs). It started as the successor of OpenCA, and builds on the experience gained while developing it as well as on our experience in large public key infrastructures.
- CA rollover: “Normal” trust center software usually does not account for the installment of a new CA certificate, thus if the CA certificate becomes invalid, a complete re-deployment has to be undertaken. OpenXPKI solves this problem by automatically deciding which CA certificate to use at a certain point in time.
- Support for multiple so-called PKI realms: Different CA instances can be run in a single installation without any interaction between them, so one machine can be used for different CAs.
- Private key support both in hardware and software: OpenXPKI has support for professional Hardware Security Modules such as the nCipher nShield or the Safenet Luna CA modules. If such modules are not available, access to a key can be protected by using a threshold secret sharing algorithm.
- Professional database support: The user can choose from a range of database backends, including commercial ones such as Oracle which are typically used in enterprise scenarios.
- Many different interfaces to the server: Humans can access the CA server using a web-interface. Embedded devices such as routers can also use the Simple Certificate Enrollment Protocol (SCEP) to talk to the server and apply for certificates - including automatic renewal. If integration into existing systems is required, REST and SOAP interfaces are also available.
- Workflow Engine: OpenXPKI aims to be extremely customizable by allowing the definition of workflows for any process you can think of in the PKI area. Typical workflows such as editing and approving certificate signing requests, certificate and CRL issuance are already implemented. Implementing your own idea is normally pretty easy by defining a workflow in a YAML configuration file and (maybe) implementing a few lines in Perl.
- I18N: Localization of the application and interfaces is easily possible.
- Self-Service application for smartcard/token personalization: A web application which allows a user to easily create and install certificates to a smartcard is available (commercial third-party component required).
- Template-based certificate details: Contrary to the typical CA system, your users do not need to know about how you would like the subject to look like - you can just ask them for the information they know (for example a hostname and port) and OpenXPKI will create the corresponding subject and subjectAlternativeNames for you. Regular expression support allows you to enforce certificate naming conventions easily.
- Interchangeble notifation backends We can of course send eMail notifications to customers and operators but if you have a heavy load of certificate requests that need additional communication with the requesters, you can attach a ticket system like [http://www.bestpractical.com/rt/ Request Tracker], which will receive updates on the certificate status.
Quickstart guide¶
OpenXPKI is an easy-to-deploy and easy-to-use RA/CA software that makes handling of certificates easy but nevertheless you should really have some basic knowledge on what a PKI is. If you just want to see “OpenXPKI in action” for a first impression of the tool, use the public demo at https://demo.openxpki.org.
Support¶
If you need help, please use the mailing list and do NOT open items in the issue tracker on GitHub. For details and additional support options have a look at http://www.openxpki.org/support.html.
Docker¶
We also provide a docker image based on the debian packages as well as a docker-compose file, see https://github.com/openxpki/openxpki-docker. Please read the hints in the README if you try this on Windows!
Configuration¶
The debian package come with a sample configuration, for a production setup we recommend to remove the /etc/openxpki folder created by the package and replace it with a checkout of the community branch of the configuration repository available at https://github.com/openxpki/openxpki-config. Please also have a look at the QUICKSTART.md document which has some more detailed instructions how to setup the system.
Note: The configuration is (usually) backward compatible but most releases introduce new components and new configuration that can not be used with old releases. Make sure your code version is recent enough to run the config! Starting with v3.12, the code checks its compatibility with the config itself via the system.version.depend node. We recommend to keep and maintain this in your config!
Debian Builds¶
Packages are for Debian 12 (Bookworm) / 64bit (arch amd64). The en_US.utf8 locale must be installed as the translation system will crash otherwise! The packages do NOT work on Ubuntu or 32bit systems. Packages for SLES/CentOS/RHEL/Ubuntu are available via an enterprise subscription
Start with a debian minimal install, we recommend to add “SSH Server” and “Web Server” in the package selection menu, as this will speed up the install later.
To avoid an “untrusted package” warning, you should add our package signing key (you might need to install gpg before):
wget https://packages.openxpki.org/v3/debian/Release.key -O - 2>/dev/null | tee Release.key | gpg -o /usr/share/keyrings/openxpki.pgp --dearmor
The https connection is protected by a Let’s Encrypt certificate but if you want to validate the key on your own, the fingerprint is:
gpg --print-md sha256 Release.key (Updated 2023-06-21)
F88C6BFC 07ACE167 9399CDE5 21BD9148 4F9DA3EB B38E1BFC DA670B1C C96EB501
You can also find the key on the github repository in package/debian/Release.key.
Add the repository to your source list (bookworm):
echo -e "Types: deb\nURIs: https://packages.openxpki.org/v3/bookworm/\nSuites: bookworm\nComponents: release\nSigned-By: /usr/share/keyrings/openxpki.pgp" > /etc/apt/sources.list.d/openxpki.sources
apt update
Please do not disable the installation of “recommend” packages as this will very likely leave you with an unusable system.
As OpenXPKI can run with different RDBMS, the package does not list any of them as dependency. You therefore need to install the required perl bindings and server software yourself:
apt install mariadb-server libdbd-mariadb-perl
We strongly recommend to use a fastcgi module as it speeds up the UI, we recommend mod_fcgid as it is in the official main repository (mod_fastcgi will also work but is only available in the non-free repo):
apt install apache2 libapache2-mod-fcgid
Note, fastcgi module should be enabled explicitly, otherwise, .fcgi file will be treated as plain text (this is usually done by the installer already):
a2enmod fcgid
Now install the OpenXPKI core package, session driver and the translation package:
apt install libopenxpki-perl openxpki-cgi-session-driver openxpki-i18n
use the openxpkiadm command to verify if the system was installed correctly:
openxpkiadm version
Version (core): 3.26.0
Now, create an empty database and assign a database user:
CREATE DATABASE openxpki CHARSET utf8;
CREATE USER 'openxpki'@'localhost' IDENTIFIED BY 'openxpki';
GRANT ALL ON openxpki.* TO 'openxpki'@'localhost';
flush privileges;
…and put the used credentials into /etc/openxpki/config.d/system/database.yaml:
main:
debug: 0
type: MariaDB2
name: openxpki
host: localhost
port: 3306
user: openxpki
passwd: openxpki
Starting with the v3.8 release we added a MariaDB driver that makes use of MariaDB internal
sequences instead of the emulation code and we recommend any new installations to use it!
While the MariaDB``drivers uses the old mysql binding the newer ``MariaDB2
uses the
modern mariadb perl module which is the recommended driver on modern operating systems.
Please create the empty database schema from the provided schema file. mariadb/mysql and postgresql should work out of the box, the oracle schema is good for testing but needs some extra indices to perform properly.
Example call when debian packages are installed:
cat /usr/share/doc/libopenxpki-perl/examples/schema-mariadb.sql | \
mysql -u root --password --database openxpki
If you do not use debian packages, you can get a copy from contrib/sql/
in the
config repository https://github.com/openxpki/openxpki-config.
System Setup¶
Sample / Demo Configuration¶
The debian package comes with a shell script sampleconfig.sh
that does all the work for you
(look in /usr/share/doc/libopenxpki-perl/examples/). The script will create a two-stage ca with
a root ca certificate and below your issuing ca and certs for SCEP and the internal datasafe.
It will also start the required services, you should be able to log into the system via the webbrowser using the default credentials (see section Testdrive below).
This script provides a quickstart but should never be used for production systems (it has the fixed passphrase root for all keys ;) and no policy/crl, etc config ).
Production Configuration¶
For a production setup we recommend to remove the /etc/openxpki folder that was installed by the package and use a checkout of the openxpki-config repository at.
You need to create the following keys/certificates yourself if you dont use the sampleconfig script.
- Issuing CA certificate (recommend with a Root CA on top of it)
- Internal DataVault Certificate
- Certificate for the SCEP RA
OpenXPKI supports NIST and Brainpool ECC curves (as supported by openssl) for the CA certificates, as the Datavault certificate is used for data encryption it MUST use an RSA key! You should also remove the democa realm and create a realm with a proper name (see reference/configuration/introduction.html#main-configuration).
Starting with release 3.6 the default config uses the database to store the issuing ca and SCEP tokens - if you upgrade from an older config version check the new settings in systems/crypto.yaml.
As of v3.10 the openxpiadm alias command can be used to manage the keys directly but this requires that the server is started and the directory for the keys exists, the default location is /etc/openxpki/local/keys so we need to create the directory before we proceed:
$ mkdir -p /etc/openxpki/local/keys
We also need to start the server now (there is also an init-script and systemd unit available):
$ openxpkictl start
Starting OpenXPKI...
OpenXPKI Server is running and accepting requests.
DONE.
In the process list, you should see two process running:
14302 ? S 0:00 openxpki watchdog ( main )
14303 ? S 0:00 openxpki server ( main )
If this is not the case, check /var/log/openxpki/stderr.log.
Import Root CA¶
The Root CA is outside the scope of OpenXPKI, we recommend to use clca.
As OpenXPKI needs to be able to build the full chain for any certificate, we need to import the Root CA(s) first:
$ openxpkiadm certificate import --file root.crt
DataVault Token¶
Create an RSA key with at least 3072 bits, either chose no password or the password configured for the token in your crypto.yaml. Create a self-signed certificate with this key with subject “/CN=DataVault”. You can find a usable sample config file to create an unencrypted key in the contrib folder:
$ openssl req -new -x509 -keyout vault.key -out vault.crt -days 1100 \
-config /etc/openxpki/contrib/vault.openssl.cnf
Now import the certificate and its key:
$ openxpkiadm certificate import --file vault.crt
Starting import
Successfully imported certificate into database:
Subject: CN=Internal DataVault
Issuer: CN=Internal DataVault
Identifier: YsyZ4eCgzHQN607WBIcLTxMjYLI
Realm: none
Register it as datasafe token for the democa realm and provide the matching key file to get it loaded into the right place:
$ openxpkiadm alias --realm democa --token datasafe \
--file vault.crt --key vault.key
Successfully created alias in realm democa:
Alias : vault-1
Identifier: YsyZ4eCgzHQN607WBIcLTxMjYLI
NotBefore : 2020-07-06 18:54:43
NotAfter : 2030-07-09 18:54:43
In case you have multiple realms, you need to run this command for each realm but should omit the key file for any additional realms.
You should check now if your DataVault token is working:
$ openxpkicli get_token_info --arg alias=vault-1
{
"key_name" : "/etc/openxpki/local/keys/vault-1.pem",
"key_secret" : 1,
"key_store" : "OPENXPKI",
"key_usable" : 1
}
If you do not see “key_usable”: 1 your token is not working! Check the permissions of the file (and the folders) and if the key is password protected if you have the right secret set in your crypto.yaml!
Issuing CA Token¶
The creation and management of the Issuing CA keys and certificates themselves is not part of OpenXPKI, you need to have the keys and certificates at hand before you proceed. The keys must either be unprotected or use the secret referenced in the realms crypto.yaml.
The openxpkiadm alias command offers a shortcut to import the certificate, register the token and store the private key. Repeat this step for all issuer tokens in all realms. The system will assign the next available generation number and create all required internal links. In case you choose the filesystem as key storage the command will write the key files to the intended location but requires that the folder exist (/etc/openxpki/local/keys/<realm>):
openxpkiadm alias --realm democa --token certsign \
--file democa-signer.crt --key democa-signer.pem
If the import went smooth, you should see something like this (ids and times will vary):
$ openxpkiadm alias --realm democa
=== functional token ===
vault (datasafe):
Alias : vault-1
Identifier: lZILS1l6Km5aIGS6pA7P7azAJic
NotBefore : 2015-01-30 20:44:40
NotAfter : 2016-01-30 20:44:40
ca-signer (certsign):
Alias : ca-signer-1
Identifier: Sw_IY7AdoGUp28F_cFEdhbtI9pE
NotBefore : 2015-01-30 20:44:40
NotAfter : 2018-01-29 20:44:40
=== root ca ===
current root ca:
Alias : root-1
Identifier: fVrqJAlpotPaisOAsnxa9cglXCc
NotBefore : 2015-01-30 20:44:39
NotAfter : 2020-01-30 20:44:39
upcoming root ca:
not set
An easy check to see if the signer token is working is to create a CRL:
$ openxpkicmd --realm democa crl_issuance
Workflow created (ID: 511), State: SUCCESS
Adding the Webclient¶
The package installs a default configuration for apache but requires that you configure a tls certificate and setup the configuration for the webui session storage.
TLS Setup¶
Create a TLS certificate (self-signed or from an external PKI) and copy the key to /etc/openxpki/tls/private/openxpki.pem and the certificate to /etc/openxpki/tls/endentity/openxpki.crt.
The default configuration also offers TLS client authentication. You need to place a copy of your root certificate in /etc/openxpki/tls/chain/ and run c_rehash /etc/openxpki/tls/chain/ to make it available for chain construction in apache. If you don’t want to use client authentication you must remove the SSLCACertificatePath and SSLVerify* options as the webserver will not start if this path is empty.
Session Storage¶
The default configuration now uses a database backend to store the webui session information. Please review the section [session] and [session_driver] in the file /etc/openxpki/webui/default.conf. It is strongly advised to use a dedicated user here with access only to the frontend_session table for security reasons. You can even put this on a different database as the information is not used by the backend.
If you have a single node setup, you can switch to the filesystem based driver.
Module Setup¶
Ensure that fcgid is enabled (a2enmod fcgid
).
Testdrive¶
You should now be able to (re)start the apache server:
$ service apache2 restart
Navigate your browser to https://yourhost/openxpki/. If your browser asks you to present a certificate for authentication, skip it. You should now see the main authentication page.
The sample configuration comes with a predefined handler for a local user database and also a set of tests accounts. If you start with the configuration repository, the password for all accounts is openxpki, if you start with the debian package the password is randomized during setup, you will see it on the console during install and can find it in clear text in /etc/openxpki/config.d/realm.tpl/auth/handler.yaml
The usernames are alice and bob (users) and rob, rose and raop (operators). To setup your local user database have a look at the files in the auth directory and the reference/configuration/realm.html#authentication
- Login as User (Username: bob, Password: <see above>)
- Go to “Request”, select “Request new certificate”
- Complete the pages until you get to the status “PENDING” (gray box on the right)
- Logout and re-login as RA Operator (Username: raop, Password: <see above> )
- Select “Home / My tasks”, there should be a table with one request pending
- Select your Request by clicking the line, change the request or use the “approve” button
- After some seconds, your first certificate is ready :)
- You can download the certificate by clicking on the link in the first row field “certificate”
- You can now login with your username and fetch the certificate
Troubleshooting¶
If you only get the “Open Source Trustcenter” banner without a login prompt, make sure that the fcgi module is properly loaded and available. To see the output of the wrapper script, it might be helpful to use the browsers developer console (F12 or CTRL+F12 on most browsers).
If you get an internal server error, make sure you have the en_US.utf8 locale installed
(locale -a | grep en_US
)!
For further investigation, check /var/log/openxpki/webui.log and /var/log/apache/error.log.
Enabling the SCEP service¶
SCEP RA Certificate¶
Create a certificate to be used as SCEP RA, this is usually a TLS Server certificate from the CA itself or signed by an external CA. Import the certificate and register it as SCEP RA token:
openxpkiadm alias --realm democa --token scep \
--file scep.crt --key scep.pem
Note: Each realm needs his own SCEP token so you need to run this command any realm that provides an SCEP service. It is possible to use the same SCEP token in multiple realms.
Install SCEP Wrapper¶
Starting with v3.18, the default configuration uses a pure perl implementation for the SCEP server so there is no need to install any additional tools anymore.
If you run an older configuration or want to stick with LibSCEP for any reason, you have to install the library and perl bindings with:
apt install libcrypt-libscep-perl libscep
The remaining SCEP logic is already included in the core distribution. The package
installs a wrapper script into /usr/lib/cgi-bin/ and creates a suitable alias in
the apache config redirecting all requests to http://host/scep/<any value>
to
the wrapper.
A default config is placed at /etc/openxpki/scep/default.conf. For a testdrive,
there is no need for any configuration, just call http://host/scep/scep
.
The system supports getcacert, getcert, getcacaps, getnextca and enroll/renew - the
shipped workflow is configured to allow enrollment with password or signer on behalf.
The password has to be set in scep.yaml
, the default is ‘SecretChallenge’.
For signing on behalf, use the UI to create a certificate with the ‘SCEP Client’
profile - there is no password necessary. Advanced configuration is described in the
scep workflow section.
The best way for testing the service is the sscep command line tool (available at e.g. https://github.com/certnanny/sscep).
Check if the service is working properly at all:
mkdir tmp
./sscep getca -c tmp/cacert -u http://yourhost/scep/scep
Should show and download a list of the root certificates to the tmp folder.
To test an enrollment:
openssl req -new -keyout tmp/scep-test.key -out tmp/scep-test.csr -newkey rsa:2048 -nodes
./sscep enroll -u http://yourhost/scep/scep \
-k tmp/scep-test.key -r tmp/scep-test.csr \
-c tmp/cacert-0 \
-l tmp/scep-test.crt \
-t 10 -n 1
Make sure you set the challenge password when prompted (default: ‘SecretChallenge’). On current desktop hardware the issue workflow will take approx. 15 seconds to finish and you should end up with a certificate matching your request in the tmp folder.
Support for Java Keystore¶
OpenXPKI can assemble server generated keys into java keystores for
immediate use with java-based applications like tomcat. This requires
a recent version of java keytool
installed. On debian, this is
provided by the package openjdk-7-jre
. Note: You can set the
location of the keytool binary in system.crypto.token.javajks
, the
default is /usr/bin/keytool.
Upgrading OpenXPKI¶
We try hard to build releases that do not break old installations but sometimes we are forced to make changes that require manual adjustment of existing config or even the database schema.
This page provides a summary of recommended and mandatory changes. Recommended items should be done but the installation will continue to work. Mandatory items MUST be done, as otherwise the system will not behave correctly or even will not start.
For a quick overview of config changes, you should always check the config repository at https://github.com/openxpki/openxpki-config.
Release v3.12¶
Important: a configuration update is required when upgrading to v3.12
Major rework of the authentication layer - the handlers External and ClientSSO that were also referenced in the default configuration (but of no real use in the default setup) have been removed from the code tree. A similar functionality is available via the new handlers NoAuth and Command. In case you have those handlers as “leftovers” of the default configuration you should just remove them. If you have used them, please adjust the configuration before you upgrade.
Release v3.x¶
To upgrade from v2 or an earlier v3 installation to v3 please see the Upgrade document in the openxpki-config repository.
In case you have written your own code or used the command line tools please note that the old API was removed, and some output formats have changed! You can find the API documentation as “perldoc” the implementation classes (located in OpenXPKI::Server::API2::Plugin).
Release v2.3¶
The config parameters for the ClientX509 authentication handler have changed. In case you left the file “handler.yaml” and “stack.yaml” in realm/democa/auth/ unchanged you MUST remove or change the block for the “ClientX509” handler as the new/fixed handler will not work with the old config and OpenXPKI will not start at all!
Release v2.x¶
Upgrading from v1.x to v2.x requires some manual changes!
You MUST upgrade your database schema:
ALTER TABLE `certificate`
ADD `revocation_time` int(10) unsigned DEFAULT NULL,
ADD `invalidity_time` int(10) unsigned DEFAULT NULL,
ADD `reason_code` varchar(50) DEFAULT NULL,
ADD `hold_instruction_code` varchar(50) DEFAULT NULL;
UPDATE `crr` crr LEFT JOIN certificate crt USING (identifier)
SET crt.reason_code = crr.reason_code,
crt.revocation_time = crr.revocation_time,
crt.invalidity_time = crr.invalidity_time,
crt.hold_instruction_code = crr.hold_code;
ALTER TABLE `workflow_history`
ADD`workflow_node` varchar(64) DEFAULT NULL;
ALTER TABLE `crl`
ADD `crl_number` decimal(49,0) DEFAULT NULL,
ADD `items` int(10) DEFAULT 0,
ADD KEY `crl_number` (`issuer_identifier`,`crl_number`);
You SHOULD copy over the new default workflows, the old default workflows SHOULD continue working but some of the workflow classes have changed, so in case you made extensions please check the configuration for deltas!
In case you use SCEP please note that the definition of the workflow to use has moved from the “outer” wrapper configuration to the “inner” configuration file inside the realm. You should also switch from the old workflow type “enrollment” to the new “certificate_enroll” which has basically the same functionality but a lot better error handling and extensions. Note that the format of the workflow configuration file was also changed! Check the provided samples for details.
Release v1.19¶
Warning We changed the internal serialization format which also affects the workflow persistence layer. Workflows or data pool structures that are created or modified will use the new serialization format which cannot be read by older versions! So be aware that a downgrade or parallel operation of new and old release versions is not possible!
Release v1.18¶
Logging¶
We removed the internal, hardcoded pattern formatter for the log lines and replaced it with native Log4perl patterns using Log4perl MDC variables to give you more control on what and where to write to. If you do not adjust your configs, you will still get your logs but information on packages, etc. which was hardcoded before is now gone. Check the new sample log.conf for the new format and logging options.
Also note that the timestamps used in the application_log and audittrail table are now written as epoch with microseconds as decimal part.
Sessions¶
There is a new session handler to get rid of filesystem sessions. The frontend can write back the session information to the backend while the backend can use the database to store the session data. The provided example configuration uses those new handlers as defaults, but the code still uses the old file based sessions if you do not explicitly set the new ones. Note that you must create the sessions table yourself when upgrading:
CREATE TABLE IF NOT EXISTS `session` (
`session_id` varchar(255) NOT NULL,
`data` longtext,
`created` int(10) unsigned NOT NULL,
`modified` int(10) unsigned NOT NULL,
`ip_address` varchar(45) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `session`
ADD PRIMARY KEY (`session_id`), ADD INDEX(`modified`);
If you use backend sessions, please also set the “cookey” secret phrase to encrypt the session cookies in the webui config. Otherwise, a person with access to the server logs can very easily hijack running sessions!
Release v1.13¶
The default config now uses /var/log/openxpki/ as log directory. It is no problem to leave your log files where there are but you need to fix the permissions on the frontend logs after running the update:
cd /var/openxpki/; chown www-data webui.log scep.log soap.log rpc.log
We will fix this in the Debian update with the next release.
Release v1.11¶
We put access to workflow log/history/context under access control. If you want your users/operators to have access to those items, you MUST add the new acl items to your workflow definitions:
acl:
RA Operator:
creator: any
fail: 1
resume: 1
wakeup: 1
history: 1
techlog: 1
context: 1
If you are using the SOAP revocation interface or want to use the new RPC revocation interface, you MUST add a new field to the inital action.
Add the file config.d/realm/democa/workflow/global/field/interface.yaml to your config tree. In config.d/realm/democa/workflow/def/certificate_revocation_request_v2.yaml add the field “interface” to the list of “input” fields of “create_crr”.
Release v1.10¶
Please update your database schema:
DROP TABLE IF EXISTS `seq_application_log`;
CREATE TABLE IF NOT EXISTS `seq_application_log` (
`seq_number` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`dummy` int(11) DEFAULT NULL,
PRIMARY KEY (`seq_number`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `application_log`;
CREATE TABLE IF NOT EXISTS `application_log` (
`application_log_id` bigint(20) unsigned NOT NULL,
`logtimestamp` bigint(20) unsigned DEFAULT NULL,
`workflow_id` decimal(49,0) NOT NULL,
`priority` int(11) DEFAULT 999,
`category` varchar(255) NOT NULL,
`message` longtext,
PRIMARY KEY (`application_log_id`),
KEY (`workflow_id`),
KEY (`workflow_id`,`priority`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Append “DBI” for the application logger in /etc/openxpki/log.conf:
log4perl.category.openxpki.application = INFO, Logfile, DBI
Setup and Configuration
Overview¶
Main configuration¶
The configuration of OpenXPKI consists of two, fundamental different, parts. There is one global system configuration, which holds information about database, filesystem, etc. where the system lives. The second part are the realm configurations, which define the properties of the certificates within the realm. Each pki realm has its own, independant configuration and is isolated from other realm, so you can run instances with different behaviour with one single OpenXPKI server.
We ship the software with a set of YaML files, and we recommend to keep the given layout. The following documentation uses some notations you should know about.
- Configuration items are read as a path, which is denoted as a string with the path elements seperated by a dot, e.g.
system.database.main.type
. - The path is assembled from the directory, the name of the configuration file, the path of the element in the YaML notation. The value from the example above can be found in the directory
system
, filedatabase.yaml
, sectionmain
, keytype
. - All paths except those starting with system or realm refer to the configuration of a particular realm. The root node for building the path is the realm’s directory found at
realm/<name of the realm>
.
Config versioning¶
This idea was dropped, configuration is now read freshly from the filesystem at every restart of the daemon.
Activate the new configuration¶
To activate a new config without a restart, you need to do a reload:
openxpkictl reload
You can also just send a SIGHUP
to the main process or restart the dameon.
IMPORTANT
Those parts of the system are preloaded on server init and need a restart to load a new configuration.
- worflow configuration
- authentication handlers
- database settings
- daemon settings (never change anything below system.server while the dameon is running as you might screw up your system!)
Config Caching and Signing¶
Instead of reading the config from the filesystem freshly on each startup, there is an option to serialize the config tree into a blob which offers the option to use PKCS7 signatures to verify the configuration.
Transform config tree into blob¶
Use openxpkiadm buildconfig --config path/to/config.d
to generate the
blob. The default target is a file config.oxi
in the current directory,
you specify and alternate location with --target myconfig.blob
.
To sign the config blob, you need to add --key
and --cert
pointing
to the PEM encoded key/certificate files. Its good practice to also add the
chain certificates to verify the signer up to the root, use --chain
to
point to a file holding the concatenated PEM block of the issuers to be
added.
Enable config bootstrap¶
You need to create a node named bootstrap
on the top level of the
configuraion and should remove any other config items. The easiest way
to achieve this is to wipe anything from /etc/openxpki/config.d/
and place a file called bootstrap.yaml here:
# this is the only mandatory option
LOCATION: /home/pkiadm/config.oxi
# This is the default and should be left empty unless overridden
# class: OpenXPKI::Config::Loader
# if you want to use signed configs, set ONE of the ca* options
# Path holding the certificates as files (filemame = hash)
# ca_certificate_path: /etc/openxpki/ca/config.certs/
# All certificates in one file
# ca_certificate_file: /etc/openxpki/ca/config.pem
# temp dir, required to create files to perform signature verification
# tmpdir: /tmp
# path to openssl
# openssl: /usr/bin/openssl
Configure signature verification¶
Signature is validated using openssl smime -verify
. If you have placed
the chain certificates into the config blob, it is sufficient to have the
root certificates here, if not, make sure you have all certificates here
to create the full chain. Directory needs to hold the certificates in the
well-known openssl hashed filename format, the single file must hold the
concatenated PEM blocks.
Note: Currently we do not make any checks on the certificate itself so any certificate from the given roots/chains can be used for signing so it is recommended to setup a dedicated CA for the config signature. We are working on making the signer authorization pattern used in other parts of the system available for config signatures with one of the next releases.
Logging¶
OpenXPKI uses Log4perl as its primary system log. Logging during startup and in critical situations is done via STDERR.
Global¶
This document give a brief overview over the system configuration items. You find those settings in the files in the <configfir>/config.d/system
directory.
Database¶
Configure the settings for the database layer. You need at least a configuration for the main database. It is possible to provide a seperate database for logging purposes, just copy the complete block from system.database.main
to system.database.logging
and adjust as required. If you don’t configure a logging database, the main database is used.
The general configuration block looks like:
main:
type: supported driver name
name: name of the database
host: host
port: port
user: database user
passwd: database credential
namespace: namespace (to be used with oracle)
environment:
db_driver_env_key: value
driver:
LongReadLen: 10000000
OpenXPKI supports MariaDB (MySQL), PostgreSQL and Oracle.
The namespace parameter is used only by the Oracle driver.
Options given to driver
are passed to DBI as extra parameters.
Check perldoc OpenXPKI::Server::Database::Driver::<type> for more info on the parameters.
The recommended driver is MariaDB2
which uses the perl MariaDB driver module while
MariaDB
internally uses the old mysql
driver of perl. Depending on the OS and perl
version used this might just be an alias but we have also seen very strange issues here.
System¶
Settings about filesystem, daemon and services to start. Located at system.server
os related stuff
i18n locale settings:
i18n:
locale_directory: path to the gettext locales on your system
default_language: supported locale (e.g. en_US.utf8)
Location of the locale files and the default language used. If you set another language than C
, make sure you have the correct po-files installed, otherwise OpenXPKI won’t even start! This usually only affects logging and system messages as most of the client related output uses the locale settings from the client session. We recommend using C as default.
daemon settings
Those settings determine the properties of the OpenXPKI daemon openxpkid.:
name: label for your process list, useful if you are running multiple servers.
user: Unix user to run as (numeric or name)
group: Group to run as (numeric or name)
socket_file: Location of the communication socket.
pid_file: Location of the pid file.
environment:
key: value
log4perl: path to your Log4perl configuration file (the primary system logger).
stderr: File to redirect stderr to after dettaching from console.
tmpdir: Location for temporary files, writable by the daemon.
session:
directory: Directory to store the session information.
lifetime: Lifetime of the sessions on the server side.
The socket, pidfile and stderr are created during startup while running as root. The directory must exist, be writeable by root and accessible by the user the daemon runs as. The tmpdir must be writable by the daemon user, it can be a ramfs but can grow large in high volume environments.
system internals
transport:
Simple: 1
The transport setting is reserved for future use, leave it untouched.
service:
Default:
enabled: 1
timeout: 120
SCEP:
enabled: 1
The service block lists all services to be enabled, the key is the name of the service, the enabled key is supported by all services, for all other parameters consult the concrete service documentation (perldoc OpenXPKI::Service::<ServiceName>).
multi-node support
shift: 8
node:
id: 0
data_exchange:
TODO - this is not used yet
Server Type (Fork vs. PreFork)¶
The default is Fork
which create a new child on every incoming
connection, handles the current request and exits. The webui resuses the
backend connection as long as the CGI wrapper is running but most of the
other clients don’t and there require a new fork on every request.
To reuse existing childs you can set the server type to prefork which forkes of child process on server startup and reuses them for multiple connections. In server.yaml uncomment this block:
type: PreFork
prefork:
min_servers: 5
min_spare_servers: 5
max_servers: 25
max_spare_servers: 10
The option is optional, if not provided the defaults of the Net::Server module are used.
Watchdog¶
The openxpkid daemon forks a watchdog process to take care of background processes.
It is initialised with default settings, but you can provide your own values by setting them at system.watchdog
.
# How to deal with exceptions
max_exception_threshhold: 10
interval_sleep_exception: 60
max_tries_hanging_workflows: 3
# Control the wait intervals
interval_wait_initial: 60
interval_loop_idle: 5
interval_loop_run: 1
# You should not change this unless you know what you are doing
max_instance_count: 1
disabled: 0
Please see perldoc OpenXPKI::Server::Watchdog for details.
Crypto layer (global)¶
Define several parameters for the basic crypto tools.
api settings
You should not need to touch this unless you are developing your own crypto classes.
tokenapi:
certsign: OpenXPKI::Crypto::Backend::API
datasafe: OpenXPKI::Crypto::Backend::API
scep: OpenXPKI::Crypto::Tool::SCEP::API
The setting denotes the name of the perl module used as backend class when using a token of the given class. Default tokens are certsign, is used for all ca operations, and datasafe, used to internally´ encrypt data. Any tokens that are not defined here, use OpenXPKI::Crypto::Backend::API by default. If you run a scep server, you must add the line for the scep module, as it does not work with the default.
configuration of the default tokens
token:
default:
backend: OpenXPKI::Crypto::Backend::OpenSSL
api: OpenXPKI::Crypto::Backend::API
engine: OpenSSL
key_store: OPENXPKI
# OpenSSL binary location
shell: /usr/bin/openssl
# OpenSSL binary call gets wrapped with this command
wrapper: ''
# random file to use for OpenSSL
randfile: /var/openxpki/rand
pkcs7:
backend: OpenXPKI::Crypto::Tool::PKCS7
api: OpenXPKI::Crypto::Tool::PKCS7::API
javaks:
backend: OpenXPKI::Crypto::Tool::CreateJavaKeystore
api: OpenXPKI::Crypto::Tool::CreateJavaKeystore::API
If you have non-standard file locations, you might want to change the OpenSSL relevant settings here, the wrapper allows you to provide the name of a wrapper command which is commonly necessary if you use hardware security modules or other special OpenSSL eninges for your crypto operations. See the section about using HSMs for more details.
Developer note: See OpenXPKI::Crypto::TokenManager::get_system_token
PKI Realms¶
The detailed settings of each realm are given in the specific realm configuration. To use a realm you need to specify and enable it at system.realms
.
democa:
label: This is just a verbose label for your CA
You should use only 7bit word characters and no spaces as name for the realm.
Realm¶
In order to create a new realm you must create a new directory below
config.d/realm
with the internal name of the realm. While it is
possible to just create a symlink or copy from the realm.tpl directory
we recommend to read the instructions in the Quickstart document as this
approach gives you the best options for later upgrades of the
configuration.
When finished add a new section in the file system/realms.yaml
where
the new section key is identical to the new realm directory name used for
the realm directory. Change the new realm section entries to match the
desired values for the new realm.
Please note that you might need to perform additional steps based on the overall configuration options such as creating templates, static content or mapping items. Those should be outlined in the configurations setup document.
The realm configuration consists of five major parts:
- Authentication
- Configure which authentication mechanisms to use for the realm. If you use internal authentication methods, this also holds your user databases.
- Crypto layer
- Define name and path of your keyfiles and settings for the crypto tokens used.
- Profiles
- Anything related to your certificate and crl profiles
- Publishing
- How and where to publish certificates and crls
- Workflow
- Configuration data of the internal workflow engine.
Authentication¶
Authentication is done via authentication handlers, multiple handlers are combined to an authentication stack.
Stack¶
The realm’s authentication combines the configured authentication handlers to offer different authentication stacks. On the login page, the entries of the stack are shown and a user can choose between them. The stacks are configured in the file located in <realm-basedir>/auth/stack.yaml
. If, for example, one would like to enable only anonymous logins and password based logins, this file’s contents could be as follows:
Anonymous:
label: Guest Login
handler: Anonymous
type: anon
User:
label: User Login
type: passwd
handler:
- Operator Password
- User Password
In this configuration, the realm offers two login stacks, namely Anonymous and User.
The stack Anonymous uses the Handler Anonymous
and the logins
using the stack User may be performed by both handlers Operator Password
and
User Password
. Therefore, when selecting this variant, both logins with credentials
configured for Operator Password
and for User Password
are supported. You can define
any number of stacks and reuse the same handlers inside. You must define at least one stack.
Advanced Usage
Use the TLS Client Certificate from the HTTPS connection:
Certificate:
handler: Certificate
type: x509
Use the REMOTE_USER field from basic auth, optionally pass additonal ENV keys from advanced auth modules:
BasicAuth:
handler: NoAuth
type: client
envkeys:
email: AUTH_PROVIDER_email_field
Handler¶
A handler consists of a perl module, that provides the authentication mechanism. The name of the handler is used to be referenced in the stack definition, mandatory entries of all handlers is type. All handlers are defined below OpenXPKI::Server::Authentication, where type is equal to the name of the module.
Here is a list of some handlers and their configuration sets, more can be found in the sample configuration. Extensive documentation can be found in the perldoc for the classes.
Anonymous user
If you just need an anonymous connection, you can use the Anonymous handler.
Anonymous:
type: Anonymous
# the verbose name is shown in the UI
name: Guest User
System:
type: Anonymous
role: System
If no role is provided, you get the anonymous role. Do never set any other role than system, unless you exactly know what you are doing!
x509 based authentication
X509 uses the SSL client authentication feature of apache/mod_ssl. It passes the signer certificate to a validation function that cryptographically checks the chain and tests the chain against a list of trusted anchors.
The configuration is the same for both handlers (apart from the class name):
Certificate:
type: ClientX509
role: User
trust_anchor:
realm: userca
Please check perldoc OpenXPKI::Server::Authentication::X509 for details.
Password database handler
The password database handler allows to specify user/password/role pairs directly inside the configuration.
Password:
type: Password
description: I18N_OPENXPKI_CONFIG_AUTH_HANDLER_DESCRIPTION_PASSWORD
user:
John Doe:
digest: "{SSHA}TZXM/aqflDDQAmSWVxSDVWnH+NhxNU5w"
role: User
root:
digest: "{SSHA}+u48F1BajP3ycfY/azvTBqprsStuUnhM"
role: CA Operator
raop:
digest: "{SSHA}ejZpY22dFwjVI48z14y2jYuToPRjOXRP"
role: RA Operator
The passwords are hashed, the used hash algorithm is given as prefix inside the curly brackets. You should use only SSHA which is “salted sha1”. For compatibility we support plain sha (sha1), md5, smd5 (salted md5) and crypt. You can created the salted passwords using the openxpkiadm CLI tool (openxpkiadm hashpwd
). Alternatively, for batch processing, a salted sha1 password could be generated using openssl:
salt="$(openssl rand -base64 3)"
password="secretpassword"
echo -n $(echo -n "$password$salt" | openssl sha1 -binary)$salt | openssl enc -base64
Note: As of v3.10 we also directly support the format of the openssl passwd command starting with the Dollar sign.
If you plan to use static passwords for a larger amount of users, you should consider to use a connector instead:
Password:
type: Password
user@: connector:auth.connector.userdb
Define the user database file inside auth.connector.yaml:
userdb:
class: Connector::Proxy::YAML
LOCATION: /home/pkiadm/democa-userdb.yaml
The user file has the same structure as the user section above, the user names are the on the top level:
root:
digest: "{SSHA}+u48F1BajP3ycfY/azvTBqprsStuUnhM"
role: CA Operator
raop:
digest: "{SSHA}ejZpY22dFwjVI48z14y2jYuToPRjOXRP"
role: RA Operator
You can share a user database file within realms.
Authentication connectors
There is a family of authentication connectors. The main difference against other connector is, that the password is passed as a parameter and is not part of the path. Check for connectors starting with Connector::Builtin::Authentication. The connector only validates the password, therefore the role must be set in the configuration (same for all users handled by this item):
Password Connector:
type: Connector
role: User
source@: connector:auth.connector.localuser
An example config to authenticate RA Operators against ActiveDirectory using their company mail address and windows password including check of a group membership (this is just the authentication, set the role in the handler config):
raop-ad:
class: Connector::Builtin::Authentication::LDAP
LOCATION: ldap://ad.company.com
base: dc=company,dc=loc
binddn: cn=binduser
password: secret
filter: "(&(mail=[% LOGIN %])(memberOf=CN=RA Operator,OU=SecurityGroups,DC=company,DC=loc))"
External authentication
If you have a proxy or sso system in front of your OpenXPKI server that authenticates your users, the external handler can be used to set the user information:
ExternalAuth:
type: NoAuth
role: User
Crypto layer¶
group assignment¶
You must provide a list of token group names at crypto.type
to tell the system which token group it should use for a certain task. The keys are the same as used in system.crypto.tokenapi
(see Crypto layer (global)). See TODO for a detailed view how the token assignment works.
type:
certsign: ca-certsign
datasafe: vault
scep: scep
token setup¶
Any token used within OpenXPKI needs a corresponding entry in the realm’s token configuration at crypto.token
. The name of the token is the alias name you used while registering the correspondig certificate.
token:
democa-certsign:
backend: OpenXPKI::Crypto::Backend::OpenSSL
key: /etc/openxpki/local/keys/democa/ca-certsign-1.pem
# possible values are OpenSSL, nCipher, LunaCA
engine: OpenSSL
engine_section: ''
engine_usage: ''
key_store: OPENXPKI
# OpenSSL binary location
shell: /usr/bin/openssl
# OpenSSL binary call gets wrapped with this command
wrapper: ''
# random file to use for OpenSSL
randfile: /var/openxpki/rand
# Secret group
secret: default
The most important setting here is key which must be the absolute filesystem path to the keyfile. The key must be in PEM format and is protected by a password. The password is taken from the secret group mentioned by secret. See TODO for the meaning of the other options.
using inheritance
Usually the tokens in a system share a lot of properties. To simplify the configuration, it is possible to use inheritance in the configuration:
token:
default:
backend: OpenXPKI::Crypto::Backend::OpenSSL
......
secret: default
server-ca-1:
inherit: default
key: /etc/openxpki/local/keys/democa/ca-certsign-1.pem
secret: gen1pass
server-ca-2:
inherit: default
key: /etc/openxpki/local/keys/democa/ca-certsign-2.pem
Inheritance can daisy chain profiles. Note that inheritance works top-down and each step replaces all values that have not been defined earlier but are defined on the current level. Therefore you should not use undef values but the empty string to declare an empty setting.
You can use template toolkit to autoconfigure the key
property, this way you can roll over your key without modifying your configuration.
The example above will then look like:
token:
default:
backend: OpenXPKI::Crypto::Backend::OpenSSL
key: /etc/openxpki/local/keys/democa/[% ALIAS %].pem
......
secret: default
server-ca-1:
inherit: default
secret: gen1key
server-ca-2:
inherit: default
If you need to name your keys according to a custom scheme, you also have GROUP (ca-signer) and GENERATION (1) available for substitution. The certificate identifier is also available via IDENTIFIER.
token in datapool
Instead of having the tokens key files on the filesystem it is possible to store them in the datapool. Please be aware of the security implications of putting your CAs PRIVATE KEYS into the datapool which is readable by anybody with access to the database or the openxpki socket!
You must set the attribute key_store
to DATAPOOL
and provide the
name of the used datapool key using the key
attribute:
scep:
inherit: default
key_store: DATAPOOL
key: "[% ALIAS %]"
This will read the SCEP key from the datapool, the used namespace is
sys.crypto.keys
. You must import the key yourself, e.g. from the CLI:
openxpkicli set_data_pool_entry --arg namespace=sys.crypto.keys \
--arg key=scep-1 \
--arg encrypt=1 \
--filearg value=file_with_key.pem
Using the datapool encryption hides the value of the key from database admins but still exposes it in clear text to anybody with access to the command line tool! It should be obvious that you can not store the data-vault token this way as it is needed to decrypt the datapool items!
Starting with v3.8 the openxpkiadm alias
command can handle key imports
internally, you can load the certificate and key in one step:
openxpkiadm alias --realm democa --token scep \
--file democa-scep.crt --key democa-scep.pem
HSM via PKCS#11
Tokens may be maintained by HSMs as well. For HSMs a standardized interface called PKCS#11 is defined. OpenSSL supports this interface as well through its pkcs11 engine. This OpenSSL engine is supplied by the OpenSC and has to be configured in OpenXPKI.
To use PKCS#11 token in OpenXPKI the following settings has to be made:
- The engine has to be set to PKCS11. This causes OpenXPKI to use OpenSSL’s PKCS#11 engine.
- The key has to correspond to the key’s identification of the HSM. For example when the YubiHSM2 is used, the string slot_0-label_issuer_key would correspond to a stored key with the label issuer_key.
- As engine_section one can define how OpenSSL accesses the HSM.
OpenXPKI always generates OpenSSL configurations on the fly when needed and if this token is accessed, the contents of OpenSSL’s
[engine_section]
are pasted in this configuration file. To define which passphrase is used to unlock the HSM, the configuration parameter PIN should be set as shown in the example. OpenXPKI ensures to replace any occurrence of the string __PIN__ with the corresponding secret. - The value of engine_usage defines when the engine should be used. Often ALWAYS is the preferred setting.
To use PKCS#11 tokens in OpenXPKI, the backend of the token has to be set to PKCS11.:
token:
signer:
backend: OpenXPKI::Crypto::Backend::OpenSSL
key: "slot_0-label_issuer_key"
engine: PKCS11
engine_section: |
engine_id = pkcs11
dynamic_path = /usr/lib/engines/engine_pkcs11.so
MODULE_PATH = /usr/lib/x86_64-linux-gnu/pkcs11/yubihsm_pkcs11.so
PIN = __PIN__
init = 0
engine_usage: 'ALWAYS'
key_store: ENGINE
shell: /usr/bin/openssl
randfile: /var/openxpki/rand
wrapper: ''
secret: signer
The linked secret is only used to get access to the HSM. The secret used to unlock the HSM can be configured normally. For the YubiHSM2 for example a secret group that uses the authentication key 0x0001 with the password password would be the following:
secret:
signer:
label: YubiHSM password
method: literal
value: 0001password
cache: daemon
Note: To be able to use the YubiHSM2 with OpenSSL, two environment variables has to be set (YUBIHSM_PKCS11_CONF
and YUBIHSM_PKCS11_MODULE
).
If those environment variables are set when the server is started, the OpenXPKI
process inherits these values.
Secret Groups¶
A secret group maintains the password cache for your keys and PINs. You need to setup at least one secret group for each realm. The most common version is the plain password:
secret:
default:
label: One Piece Password
method: plain
cache: daemon
This tells the OpenXPKI daemon to ask for the default only once and then store it “forever”. If you want to have the secret cleared at the end of the session, set cache: session.
To increase the security of your key material, you can configure secret splitting by dividing the PIN entry into n components that are simply concatenated.
secret:
ngkey:
label: Split secret Password
method: plain
total_shares: 3
cache: daemon
If you have a good reason to put your password into the configuration, use the literal type:
secret:
insecure:
label: A useless Password
method: literal
value: my_not_so_secret_password
cache: daemon
You can also use the secret groups for other purposes, in this case you need to add “export: 1” to the group. This allows you to use the get_secret method of the TokenManager (OpenXPKI::Crypto::TokenManager) to retrieve the plain value of the secret.
Profiles¶
certificates¶
There is a TODO:link seperate section about certificate profile configuration.
certificate revocation list¶
A basic setup must provide at least a minimum profile for crl generation at crl.default
:
digest: sha1
validity:
nextupdate: +000014
renewal: +000003
The nextupdate value gives the validity of the created crl (14 days). The renewal value tells OpenXPKI how long before the expiry date of the current crl the system is allowed to create a new one. If you set this to a value larger than nextupdate, a new crl is created every time you trigger a new crl creation workflow. Note: If a certificate becomes revoked, the renewal interval is not checked.
crl at “end of life”
Once your ca certificate exceeds its validity, you are no longer able to create new crls (at least if you are using the shell modell). OpenXPKI allows you to define a different validity for the last crl, which is taken if the next calculated renewal time will exceed the validity of the ca certificate:
validity:
nextupdate: +000014
renewal: +000003
lastcrl: 20301231235900
crl extensions
The following code shows the full set of supported extensions, you can skip what you do not need:
extensions:
authority_info_access:
critical: 0
ca_issuers: http://myca.mycompany.com/[% CAALIAS.ALIAS %]/cacert.pem
ocsp:
- http://ocsp1.mycompany.com/
- http://ocsp2.mycompany.com/
authority_key_identifier:
critical: 0
keyid: 1
issuer: 1
issuer_alt_name:
critical: 0
# If the issuer has no subject alternative name, copying returns
# an empty extension, which is problematic with both RSA SecurId
# tokens and Cisco devices!
copy: 0
There are two specialities in handling the ca_issuers and ocsp entries in the authority_info_access section:
- You can pass either a list or a single scalar to each item.
- For each item, template expansion based on the signing ca certificate is available. See TODO:link for details.
The CAALIAS
hash also offers the components of the alias in GENERATION and GROUP.
Publishing¶
Publishing of certificates and crl is done via connectors (TODO:link). The default workflows look for targets at publishing.entity
and publishing.crl
. Each target can contain a list of key-value pairs where the value points to a valid connector item while the keys are used for internal logging:
entity:
int-repo@: connector:publishing.connectors.ldap
ext-repo@: connector:publishing.connectors.ldap-ext
crl:
crl@: connector:publishing.connectors.cdp
certificate publishing
The OpenXPKI packages ship with a sample configuration for LDAP publication but you might include any other connector. The publication workflow appends the common name of the certificate to the connector path and passes a hash containing the subject (subject) and the DER (der) and PEM (pem) encoded certificate.
The configuration block looks like this:
connectors:
ldap-ext:
class: Connector::Proxy::Net::LDAP::Single
LOCATION: ldap://localhost:389
base: ou=people,dc=mycompany,dc=com
filter: (|(mail=[% ARG %]) (objectCategory=person))
binddn: cn=admin,dc=mycompany,dc=com
password: secret
attrmap:
der: usercertificate;binary
create:
basedn: ou=people,dc=mycompany,dc=com
rdnkey: cn
schema:
cn:
objectclass: inetOrgPerson
values:
sn: copy:self
ou: IT Department
Let’s explain the parts.
class: Connector::Proxy::Net::LDAP::Single
LOCATION: ldap://localhost:389
base: ou=people,dc=mycompany,dc=com
filter: (|(mail=[% ARG %]) (objectCategory=person))
binddn: cn=admin,dc=mycompany,dc=com
password: secret
Use the Connector::Proxy::Net::LDAP::Single package and use cn=admin,dc=mycompany,dc=com and secret to connect with the ldap server at ldap://localhost:389 using ou=people,dc=mycompany,dc=com as the basedn. Look for an entry of class person where the mailadress is equal to the common name of the certificate.
attrmap:
der: usercertificate;binary
Publish the content of the internal key der to the ldap attribute usercertificate;binary.
create:
basedn: ou=people,dc=mycompany,dc=com
rdnkey: cn
This enables the auto-creation of non-existing nodes. The dn of the new node is create from the basedn and the new component of class “cn” set to the path-item which was passed to the connector (in our example the mailadress). You also need to pass the structural information for the node to create.
schema:
cn:
objectclass: inetOrgPerson
values:
sn: copy:self
ou: IT Department
crl publishing
The crl publication workflow appends the common name of the ca certificate to the connector path and passes a hash containing the subject (subject), the components of the parsed subject as hash (subject_hash) and the DER (der) and PEM (pem) encoded crl.
The default configuration comes with a text-file publisher for the crl:
cdp:
class: Connector::Builtin::File::Path
LOCATION: /var/www/openxpki/myrealm/crls/
file: "[% ARGS %].crl"
content: "[% pem %]"
If the dn of your current ca certificate is like “cn=My CA1,ou=ca,o=My Company,c=us”, this connector writes the PEM encoded crl to the file /var/www/openxpki/myrealm/crls/My CA1.crl
Notification¶
Notifications are triggered from within a workflow. The workflow just calls the notification layer with the name of the message which should be send, which can result in no message or multiple messages on different communication channels.
The configuration is done per realm at notification
. Supported connectors
are Mail via SMTP (plain and html) and RT Request Tracker
(using the RT::Client::REST module from CPAN). You can use an arbitrary number
of backends, where each one has its own configuration at notification.mybackend
.
Most parts of the messages are customized using the Template Toolkit. The list of available variables is given at the end of this section.
Sending mails using SMTP¶
You first need to configure the SMTP backend parameters:
backend:
class: OpenXPKI::Server::Notification::SMTP
host: localhost
port: 25
starttls: 0
username: smtpuser
password: smtpsecret
debug: 0
use_html: 0
Class is the only mandatory parameter, the default is localhost:25 without authentication. Debug enables the Debug option from Net::SMTP writing to the stderr.log which can help you to test/debug mail delivery. To use html formatted mails, you need to install MIME::Lite and set use_html: 1. The handler will fall back to plain text if MIME::Lite can not be loaded.
The mail templates are read from disk from, you need to set a base directory:
template:
dir: /home/pkiadm/democa/email/
Below is the complete message configuration as shipped with the default issuance workflow:
default:
from: no-reply@mycompany.com
reply: helpdesk@mycompany.com
to: "[% cert_info.requestor_email %]"
cc: helpdesk@mycompany.com
message:
csr_created: # The message Id as referenced in the activity
user: # The internal handle for this thread
template: csr_created_user
subject: CSR for [% cert_subject %]
prefix: PKI-Ticket [% meta_wf_id %]
images:
banner: head.png
footer: foot.png
raop: # Another internal handle for a second thread
template: csr_created_raop # Suffix .txt is always added!
to: reg-office@mycompany.com
cc: ''
reply: "[% cert_info.requestor_email %]"
subject: CSR for [% cert_subject %]
csr_rejected:
user:
template: csr_rejected
subject: CSR rejected for [% cert_subject %]
cert_issued:
user:
template: cert_issued
subject: certificate issued for [% cert_subject %]
The default section is not necessary but useful to keep your config short and readable. These options are merged with the local ones, so any local variable is possible and you can overwrite any default at the local configuration (to clear a setting use an empty string, the images hash is NOT merged recursively).
the idea of threads
You might have recognized that there are two blocks below messages.csr_created
.
Those are so called threads, which combine messages sent at different times
to share some common settings. With the first message of a thread the values given
for to, cc and prefix are persisted so you can ensure that all messages
that belong to a certain thread go to the same receipients using the same subject
prefix. Note, that settings to those options in later messages are ignored!
receipient information
The primary receipient and a from address are mandatory:
- to: The primary receipient, single value, parsed using TT
- from: single value, NOT parsed
Additional receipients and a seperate Reply-To header are optional:
- cc: comma seperated list, parsed using TT
- reply: single value, NOT parsed
All values need to be rfc822 compliant full addresses.
composing the subject
The subject is parsed using TT. If you have specified a prefix, it is automatically prepended.
composing the message body
The body of a message is read from the filename specified by template, where the
suffix ‘.txt’ is always apppended. So the full path for the message at
messages.csr_created.user
is /home/pkiadm/democa/email/csr_created_user.txt.
html messages
If you use the html backend, the template for the html part is read from csr_created_user.html. It is allowed to provide either a text or a html template, if both files are found you will get a multipart message with both message parts set. Make sure that the content is the same to avoid funny issues ;)
It is possible to use inline images by listing the image files with the images key as key/value list. The key is the internal identifier, to be used in the html template, the value is the name of the image file on disk.
With a config of:
user:
template: csr_created_user
....
images:
banner: head.png
footer: foot.png
You need to reference the image in the html template like this:
<body>
<img src="cid:banner" title="My Company Logo Banner" />
.....
<img src="cid:footer" title="My Company Logo Footer" />
</body>
The images are pulled from the folder images below the template directory, e.g. /home/pkiadm/democa/email/images/head.png. The files must end on gif/png/jpg as the suffix is used to detect the correct image type.
To test your notification config, you can trigger a test message via the command line interface:
openxpkicli send_notification --arg message=testmail --param notify_to=me@company.org
RT Request Tracker¶
The RT handler can open, modify and close tickets in a remote RT system using the REST interface. You need to install RT::Client::REST from CPAN and setup the connection:
backend:
class: OpenXPKI::Server::Notification::RT
server: http://rt.mycompany.com/
username: pkiuser
password: secret
timeout: 30
The timeout value is optional with a default of 30 seconds.
As the SMTP backend, it uses templates on disk to build the ticket contents, so we also need to set the template directory:
template:
dir: /home/pkiadm/democa/rt/
You can share the templates for SMTP and RT handler and reuse most parts of your configuration, but note that the syntax is slightly different from SMTP. Here is the complete message configuration as shipped with the default issuance workflow:
message:
csr_created: # The message Id as referenced in the activity
main: # The internal handle for this ticket
- action: open
queue: PKI
owner: pki-team
subject: New CSR for [% cert_subject %]
to: "[% cert_info.requestor_email %]"
template: csr_created
priority: 1
- action: comment
template: csr_created_comment
status: open
csr_approved:
main:
- action: update
status: working
csr_rejected:
main:
- action: correspond
template: csr_rejected
priority: 10
cert_issued:
main:
- action: comment
template: cert_issued_internals
- action: correspond
template: cert_issued
status: resolved
The RT handler also makes use of threads, where each thread is equal to one ticket in the RT system. The example uses only one thread = one ticket. Each message can have multiple threads and each thread consists of at least one action.
Create a new ticket
You should make sure that a ticket is created before you work with it! The minimum information required to open a ticket is:
action: open
queue: PKI
owner: pki-team
subject: New CSR for [% cert_subject %]
to: "[% cert_info.requestor_email %]"
The to field must be an email address, which is used to fill the requestor field in RT.
Additional fields are:
- cc: comma sep. list of email addresses to be assigned to the ticket, parsed with TT
- template: filename for a TT template, used as inital text for the ticket (.txt suffix is added)
- priority: priority level, usually a numeric value
- status: ticket status, usually one of “new”, “open”, “resolved”, “stalled”, “rejected”, and “deleted”.
comment or correspond to a ticket
The maximum configuration is:
action: comment # or "correspond"
status: open # optional
priority: 5 # optional
template: csr_created_comment # .txt is added
For comment the result of the parsed template is added to the ticket history.
For correspond the result is also mailed to the ticket receipients (this is a feature of RT, we dont send any mails).
Note: If the template parser returns an empty string, no operation is done on the ticket.
update status/priority without text
The update action allows you to set status/priority without creating a text entry in the history:
action: update
status: stalled
priority: 0
You can call update with either status or priority or both.
setting custom fields
You can set custom field values using the update action. Any key/value pair in the block (except the ones above) is considered to be a custom field. The values are parsed using TT:
action: update
priority: 3
custom-field1: My custom value
custom-field2: My other custom value
Note: This feature is untested!
closing a ticket
You can close a ticket with the above commands by setting the status-flag. For convenience there is a shortcut, setting the status to “resolved”:
action: close
Template Variables¶
The notification handler injects those values into the template parser on any invocation.
realm info
- meta_pki_realm (key of the current realm)
- meta_label (verbose realm name as defined at
system.realms.$realm.label
) - meta_baseurl (baseurl as defined at
system.realms.$realm.baseurl
)
request related context values (scalars)
- csr_serial
- cert_subject
- cert_identifier
- cert_profile
request related context values (hashes)
- cert_subject_parts
- cert_subject_alt_name
- cert_info
- approvals
misc
- creator
- requestor (real name of the requestor, if available assembled from cert_info.requestor_gname + requestor_name, otherwise the word “unknown”)
Certificate Info Plugin
The default install also provides a plugin to get detailed informations on a certificate:
[% USE Certificate %]
Serial: [% Certificate.serial(cert_identifier) %]
Hex Serial: [% Certificate.serial_hex(cert_identifier) %]
CSR: [% Certificate.csr_serial(cert_identifier) %]
Issuer: [% Certificate.issuer(cert_identifier) %]
Status: [% Certificate.status(cert_identifier) %]
Body-Subject: [% Certificate.body(cert_identifier, 'Subject') %]
The body method will return any field of the body structure offered by the get_cert api method. Fore further info check the modules documentation (OpenXPKI::Template::Plugin::Certificate).
Workflow¶
The definition of the workflows is still in the older xml format, already used in older OpenXPKI releases but its management is included into the connector now. The XML files are located in the folder named _workflow (note the underscore!) in the top level direcotry of the realm. If you are upgrading from an older installation, you can just move your old workflow*.xml files here and add an outer “openxpki” tag to the workflow.xml file.
Workflow Definition¶
Each workflow is represented by a file or directory structure below workflow.def.<name>
inside the realm configuration. The name of the file is equal to the internal name of the workflow. Each such file must have the following structure, not all attributes are mandatory or useful in all situations:
head:
label: The verbose name of the workflow, shown on the UI
description: The verbose description of the workflow, shown on the UI
prefix: internal short name, used to prefix the actions, must be unique
Must not contain any other characters than [a-z0-9]
state:
name_of_state: (used as literal name in the engine)
autorun: 0/1
autofail: 0/1
label: visible name
description: the text for the page head
action:
- name_of_action > state_on_success ? condition_name
- name_of_other_action > other_state_on_success !condition_name
hint:
name_of_action: A verbose text shown aside of the button
name_of_other_action: A verbose text shown aside of the button
button:
name_of_action:
label: Label to put on the button (default is label from action)
format: layout of the button = assigned stylesheet
# add a confirmation popup when button is pressed
confirm:
label: Headline of the popup dialog
description: Text inside the popup
confirm: label of the proceed button
cancel: label of the abort button
action:
name_of_action: (as used above)
label: Verbose name, shown as label on the button
tooltip: Hint to show as tooltip on the button
description: Verbose description, show on UI page
class: Name of the implementation class
button: Label for the submit button (default is "continue")
abort: state to jump to on abort (UI button, optional) # not implemented yet
resume: state to jump to on resume (after exception, optional) # not implemented yet
validator:
- name_of_validator (defined below)
input:
- name_of_field (defined below)
- name_of_other_field
param:
key: value - passed as params to the action class
field:
field_name: (as used above)
name: key used in context
label: The fields label
placeholder: Hint text shown in empty form elements
tooltip: Text for "tooltip help"
type: Type of form element (default is input)
required: 0|1
default: default value
api_type: Shortcut syntax to specify an OpenAPI type
api_label: Label to use in OpenAPI specification
more_key: other_value (depends on form type)
validator:
class: OpenXPKI::Server::Workflow::Validator::CertIdentifierExists
param:
emptyok: 1
arg:
- $cert_identifier
Note: All entity names must contain only letters (lower ascii), digits and the underscore.
Below is a simple, but working workflow config (no conditions, no validators, the global action is defined outside this file):
head:
label: I am a Test
description: This is a Workflow for Testing
prefix: test
state:
INITIAL:
label: initial state
description: This is where everything starts
action: run_test1 > PENDING
PENDING:
label: pending state
description: We hold here for a while
action: global_run_test2 > SUCCESS
SUCCESS:
label: finals state
description: It's done - really!
status:
level: success
message: This is shown as green status bar on top of the page
action:
run_test1:
label: The first Action
description: I am first!
class: Workflow::Action::Null
input: comment
param:
message: "Hi, I am a log message"
field:
comment: (as used above)
name: comment
label: Your Comment
placeholder: Please enter a comment here
tooltip: Tell us what you think about it!
type: textarea
required: 1
default: ''
Workflow Head¶
States¶
The action
attribute is a list (or scalar) holding the action name and the
follow up state. Put the name of the action and the expected state on success,
seperated by the >
sign (is greater than).
Actions¶
t.b.d.
Fields¶
SELECT field with options¶
type: select
option:
item:
- unspecified
- keyCompromise
- CACompromise
- affiliationChanged
- superseded
- cessationOfOperation
label: I18N_OPENXPKI_UI_WORKFLOW_FIELD_REASON_CODE_OPTION
If the label
tag is given (below option
!) the values in the drop down are
i18n strings made from label
+ uppercase(key)
, e.g
I18N_OPENXPKI_UI_WORKFLOW_FIELD_REASON_CODE_OPTION_UNSPECIFIED.
OpenAPI specific field parameters¶
api_type: Array[Str]
api_label: List of surnames
To be able to generate the OpenAPI spec the data types of all relevant input/output parameters must be defined. The most precise way to do this is to specify api_type
in a field definition.
If api_type
is not given then OpenXPKI tries to deduce the correct OpenAPI type from the field parameters format
and type
(and from the field name in some rare cases). See Perl class OpenXPKI::Server::API2::Plugin::Workflow::get_openapi_typespec
for technical details.
api_type¶
api_type
accepts a custom shortcut syntax to define OpenAPI data types. The syntax is close to the syntax used in Moose types. All type names are case insensitive.
Supported types
String
aliasStr
Integer
aliasInt
Numeric
aliasNum
Boolean
aliasBool
Array
aliasArrayRef
The type of array items may be specified in square brackets:
Array[ Str ] Array[ Str | Int ]
Object
aliasObj
,Hash
,HashRef
The object properties (i.e. hash items) may be specified in square brackets:
Object[ age: Integer, name: String ]
Type parameters/modifiers
Modifiers may be passed in brackets. Please note that those modifiers are case sensitive as they are used as-is in the OpenAPI spec.
String(format:password)
Integer(minimum: 1)
Examples
Some more complex examples of nested types:
Array[ Object[ comment:Str, names:Array[Str] ] ]
HashRef[ size:Integer(minimum:5), data:Array, positions:Array[ Integer | Numeric ] ]
Please note
- types are case insensitive
- you can insert spaces wherever you like in a type definition
api_label¶
api_label
is used as a field description in the OpenAPI spec. If not given, label
is used instead.
For an OpenAPI overview please see OpenAPI (aka Swagger).
Global Entities¶
You can define entities for action, condition and validator for global use in the corresponding files below workflow.global.
. The format is the same as described below, the global_ prefix is added by the system.
Creating Macros (not implemented yet!)¶
If you have a sequence of states/actions you need in multiple workflows, you can
define them globally as macro. Just put the necessary state and action sections
as written above into a file below workflow.macros.<name>
. You need to have
one state named INITIAL
and one FINAL
.
To reference such a macro, create an action in your main workflow and replace the
class
atttribute with macro
. Note that this is NOT an extension to the workflow
engine but only merges the definitions from the macro file with those of the current
workflow. After successful execution, the workflow will be in the state passed in the
success
attribute ofthe surrounding action.
Workflow UI Rendering¶
The UI uses information from the workflow definition to render display and input pages. There are two different kinds of pages, switches and inputs.
Action Switch Page¶
Used when the workflow comes to a state with more than one possible action.
headline
Concated string from state.label + workflow.label
descriptive intro
String as defined in state.description, can contain HTML tags
workflow context
By default a plain dump of the context using key/values, array/hash values are converted to a html list/dd-list. You can define a custom output table with labels, formatted values and even links, etc - see the section “Workflow Output Formatting” fore details.
button bar / simple layout
One button is created for each available action, the button label is taken from action.label. The value of action.tooltip becomes a mouse-over label.
button bar / advanced layout
If you set the state.hint attribute, each button is drawn on its own row with a help text shown aside.
Form Input Page¶
Used when the workflow comes to a state where only one action is available or where one action was choosen.
headline
Concated string from action.label (if none is given: state.label ) + workflow.label
descriptive intro
String as defined in action.description, can contain HTML tags
form fields
The field itself is created from label, placeholder and tooltip. If at least one form field has the description attribute set, an explanatory block for the fields is added to the bottom of the page.
Markup of Final States¶
If the workflow is in a final state, the default is to render a colored status bar on with a message that depends on the name of the state. Recognized names are SUCCESS, CANCELED and FAILURE which generate a green/yellow/red bar with a corresponding error message. The state name NOSTATUS has no status bar at all.
If the state does not match one of those names, a yellow bar saying “The workflow is in final state” is show.
To customize/suppress the status bar you can add level and message to the state definition (see above).
Workflow Output Formatting¶
Buttons¶
Key/Value Grid¶
redirect¶
Creates an immediate redirect command with value as location. The location must be an ember route, e.g. workflow!start!system_status.
If the value is a hashref, you can also show a status message (message, level) and define a pause interval (pause - in seconds). The redirect target must be in the target key, if not set the redirect target is the current workflow.
WebUI components¶
Menustructure, search options and some additional items can be configured based on the user role using the files located in the uicontrol/ folder. There can be one file for each role, if no role file is found, the configuration from _default.yaml is loaded. Note that there is no inheritance, so you always need to provide a full file per role.
Format Result List¶
Those options apply to (almost) all sections that deal with result lists for workflows or certificates.
Column Layout¶
Add a section cols
to you definition block:
cols:
- label: I18N_OPENXPKI_UI_WORKFLOW_SEARCH_SERIAL_LABEL
field: WORKFLOW_SERIAL
- label: I18N_OPENXPKI_UI_WORKFLOW_SEARCH_UPDATED_LABEL
field: WORKFLOW_LAST_UPDATE
- label: I18N_OPENXPKI_UI_WORKFLOW_STATE_LABEL
field: WORKFLOW_STATE
- label: I18N_OPENXPKI_UI_CERTIFICATE_SUBJECT
field: context.cert_subject
- label: I18N_OPENXPKI_UI_WORKFLOW_FIELD_TRANSACTION_ID_LABEL
template: "[% context.transaction_id %]"
Label and either field or template are mandatory. Optional keys are sortkey, which is required to use sorting together with templates, and format which adds a formatting rule such as “timestamp” or “certstatus” (See the WebUI Page API Reference for all available formats).
Pager¶
The pager has default settings in the code but you can provide your own values:
pager:
pagesizes: 10, 20, 50, 100
pagersize: 5
The pagesizes parameter is responsible for the “Items per page” selector. The pagersite parameter refers to the number of itesm in the page selector.
Certificate search mask¶
The four fields Subject, Subject. Alt Name, Profile and Status are fixed. You can add additional that are combined to a cloneable dual-select field to search for data in the certificate_attributes tables.
Add a block named “certsearch” to the uicontrol file:
certsearch:
default:
attributes:
- label: I18N_OPENXPKI_UI_WORKFLOW_FIELD_ENTITY_LABEL
key: meta_entity
- label: I18N_OPENXPKI_UI_SEARCH_REQUESTOR_NAME_LABEL
key: meta_requestor
pattern: '*%s*'
operator: inlike
- label: I18N_OPENXPKI_UI_SEARCH_REQUESTOR_EMAIL_LABEL
key: meta_email
operator: in
transform: lower
Label and key are mandatory, key is the attributes key to be found in certificate_attributes, as of v1.19 the default operator is “IN”, so multiple values given for the same key are “ORed” (up to 1.18 this was AND which confused most users).
Possible operators are LIKE and EQUAL (values are ANDed!) or the special “INLIKE” (LIKE pattern with values ORed).
The transform and pattern keyword allow preprocessing of the input values
prior passing it to the SQL engine. Transform can be upper
or lower
which applies the uppercase/lowercase method to the value, pattern is used
with sprintf:
$val = sprintf($pattern, $val);
Each transformation is applied individually on each value.
Workflow search mask¶
This is the same as for the certificate search mask, the uicontrol key is
wfsearch
, queries are executed against the workflow_attributes table.
Tasklist¶
The page “My Tasks” can hold multiple blocks showing a list of workflows. Minimal configuration for one item looks like:
tasklist:
- label: I18N_OPENXPKI_UI_TASKLIST_PENDING_REVOCATION_LABEL
description: I18N_OPENXPKI_UI_TASKLIST_PENDING_REVOCATION_DESCRIPTION
query:
TYPE:
- certificate_revocation_request_v2
STATE:
- PENDING
Label and description are shown on top of the result table, anything below
query
is handed as parameter to the search_workflow_instances API method.
If the result set of the query is empty, the default behaviour is to display
a default “No result” test. You can customize this text using the ifempty
parameter:
- label: I18N_OPENXPKI_UI_TASKLIST_PENDING_ENROLLMENT_LABEL description: I18N_OPENXPKI_UI_TASKLIST_PENDING_ENROLLMENT_DESCRIPTION ifempty: Sorry but there is nothing to do today…
If you dont want to show an empty the result at all, pass the special word
hide
:
- label: I18N_OPENXPKI_UI_TASKLIST_PENDING_ENROLLMENT_LABEL
description: I18N_OPENXPKI_UI_TASKLIST_PENDING_ENROLLMENT_DESCRIPTION
ifempty: hide
Certificate profiles¶
A certificate profile is the blueprint that determines all technical aspects of the certificate such as subject pattern, key usages and other extensions.
Naming¶
The internal name of the profile is the name of the node in the configuration layer. If you keep the sample structure each profile is in a single file in the profile directory, so the name of the profile is the name of the file.
You can add label and description to the profile, which is used for display purpose on the WebUI frontend only, it has no effect on the actual certificate.
Validity¶
The validity is usually defined by a relative time specification of the format +YYMMDDhhmmss, (e.g. +0006 for six month, see OpenXPKI::DateTime):
validity:
notafter: +0006
The actual value is determined at the moment the PKI really signs the request. You can also add a notbefore date, in this case the notafter date is calculated relative to notbefore:
validity:
notbefore: +000001
notafter: +0006
Above example will create a certificate with a notbefore 24 hours ahead of the time of issuance and ends 6 months + 1 day later.
It is also possible to give an absolute date as YYYYMMDDhhmmss.
Styles¶
t.b.d
Subject and Process Information¶
OpenXPKI can collect meta information based on the selected profile and has a templating engine to build subject and subject alternative name sections (SAN) from the input data in many different ways.
The input fields are summarized in three groups: subject, san and info:
00_basic_style:
label: I18N_OPENXPKI_UI_PROFILE_BASIC_STYLE_LABEL
description: I18N_OPENXPKI_UI_PROFILE_BASIC_STYLE_DESC
ui:
subject:
- hostname
- hostname2
- port
san:
- san_ipv4
info:
- requestor_gname
- requestor_name
- requestor_email
- requestor_affiliation
- comment
Each section can hold any number of fields, each field is defined by a set of options:
id: hostname
label: I18N_OPENXPKI_UI_PROFILE_HOSTNAME
placeholder: fully.qualified.example.com
tooltip: I18N_OPENXPKI_UI_PROFILE_HOSTNAME_TOOLTIP
description: I18N_OPENXPKI_UI_PROFILE_HOSTNAME_DESC
type: freetext
preset: "[% CN.0.replace(':.*','') %]"
match: \A [A-Za-z\d\-\.]+ \z
width: 60
The definition can be placed in the node template inside the profile file or globally in the template directory.
To not break legacy configurations, the placeholder can be set using “default” as keyword. The tooltip is set to description if missing.
Field Definition¶
- id
- the key used when this item is written into the workflow
- label
- the label shown next to the input field
- description
- description, shown as tooltip
- type
- the type of the field, freetext or select
- option
- list of options for the select field (used value is equal to the label)
- preset
in case a CSR is uploaded by the user, you can use parts of it to prefill the fields. Items from the subject can be referenced by the name of the component, items from the subject alternative name section are prefixed with the string SAN_, e.g. SAN_DNS. Note that all keys are uppercased and all items are arrays regardless of the number of items found!
There are several options for preprocessing the items.
First item of type CN:
preset: CN.0
All items of type OU (creates on field per item):
preset: OU.X
Use templating to extract left side of CN up to the first colon:
preset: "[% CN.0.replace(':.*','') %]"
Use templating to create a list of items, the pipe symbol is used as seperator:
preset: "[% FOREACH ou = OU %][% ou %]|[% END %]"
- match
- a regex pattern that is applied to the user input for validation
- width
- size of the field - not implemented yet, definition might change.
- placeholder
- A text which is shown as placeholder in the input field (this value is NOT a default value for the field)
- renew
How to handle this field during a certificate renewal request. Can be one of:
keep: the field is set to the existing value and can not be changed preset: the field is set to the existing value but can be changed clear: the current value is unset
Subject Rendering¶
The full distinguished name and the Subject Alternative Name items are created using template toolkit rules from the information that have been collected from the input fields in the “subject” step:
subject:
dn: CN=[% hostname.lower %][% IF port AND port != 443 %]:[% port %][% END %],DC=Test Deployment,DC=OpenXPKI,DC=org
san:
DNS:
- "[% hostname.lower %]"
- "[% FOREACH entry = hostname2 %][% entry.lower %] | [% END %]"
The name of the variable it the one given as “id” in the field definition, all non empty values are available for DN and SAN rendering.
If you have provided an extra SAN section in the input fields definition, those are merged into the SAN part WITHOUT any parsing “as is”.
Extensions¶
t.b.d.
Key Parameters¶
OpenXPKI supports serverside key generation as well as PKCS10 upload. For both cases you can control what algorithms and parameters are allowed, even on a per profile basis. The default configuration has the key definition in the default.yaml file.
Basic definition of allowed key and encryption algorithms:
key:
# Supported key algorithms (details need to be defined below!)
alg:
- rsa
- ec
- dsa
# Supported encryption algorithms (as taken by openssl)
enc:
- aes256
- _3des
- idea
# one of escrow, server, client, both
# escrow is not implemented in workflows, yet!
generate: both
For RSA and DSA, you need to define the allowed key sizes in bits:
- rsa:
- key_length:
- 2048
- 4096
- _1024
Those values are used for the key generation dialog as well as for the validation of uploaded PKCS10 files. Values with an underscore are hidden from the UI.
For ECC, you need to specify the curve names:
ec:
curve_name:
- prime256v1
- secp384r1
- secp521r1
The possbile “named” curves are limited by the ones supported by Crypt::PKCS10 at the moment. For NIST P-192/256 you can use either the secpXXXr1 or primeXXXv1 alias.
Enrollment Workflow¶
The enrollment workflow is used by the SCEP, EST and RPC interface. The details how to connect the workflow to the API differ but the general configuration and operational modes are the same for all three subsystems.
Operational Modes¶
It is important to understand that the default workflow has three operational modes which are autodetected based on parameters of the request. It is a common problem that the workflow does not behave as excepted because you got into the wrong operational mode due to a non-compliant client (e.g Cisco ASA).
Initial Enrollment¶
Anonymously request a certificate for the first time - requires that the SCEP request is self-signed, which means the certificate used for the outer signature must match the key of the CSR and the subject of the request must match the subject of the signer certificate (which is a self-signed certificate in this case). Especially Ciso ASA fails here as the certificate subject does not match the request subject.
For EST and RPC this is equal to an unauthenticated/unsigned TLS request.
Renewal¶
Request renewal by sending a new request signed with the existing certificate. This requires that the full subject of request and signer certificate matches! With the default profiles the PKI will enforce the DC/O parts of the entity certificates so this is a common problem when new certificates are created using the old configuration. Best strategy is to create the new request from the old certificate to ensure the subjects match. Please note that reuse of keys is not supported, you must generate a new key for each new request.
As you can not use a TLS server certificate to authenticate as client the workflow supports an approach names “surrogate certifcates” which requires that you create a look-alike self-signed certificate with the proper key usage. Have a look at the perldoc of the EvaluateSignerTrust activity for more details.
Enrollment On Behalf¶
Request a certificate with the help of a Trusted Third Party - the request is signed using a certificate issued from the PKI which is qualified as “Authorized Signer (see below)” for the given endpoint. This branch is always choosen if the subject of request and signer do not match, so it is often hit by accident when Renewal or Initial Enrollment are made with “wrong” subjects.
For SCEP the signature on the SCEP PKCS7 container is the TTP, for EST/RPC the TTP is the TLS client certificate used to make the connection.
Workflow Logic¶
The workflow validates incoming requests against five stages. Parameters are described in detail in the section on policy settings.
- technical parameters:
- Check if key algorithm, key size and hash algorithm match the policy. If any of those checks fails, the request is rejected.
- authentication:
- A request can either be self-signed and provide a challenge password or use an HMAC for authentication or is signed by a trusted certificate (renewal or “signer on behalf”). You can also disable authentication or dispatch unauthorized requests to an operator for review.
- subject duplicate check:
- The database is checked for valid certificates with the same subject. If issuing the certificate would exceed the configured maximum count, the request is dispatched to an operator wo can either reject the request or take actions to meet the policy.
- eligibility:
- The basic idea is to check requests based on the subject or additonal parameters against an external source to see if enrollment is possible. The check counts against the approval point counter, the workflow does not take any special action if the check fails.
- approval point:
- The first four stages are usually run in one step when the request hits the server. Before the certificate is issued, the request must have a sufficient number of approval points. Each operator approval is worth one point. A passed eligibility check is also worth one.
Sample Configuration¶
The workflow fetches all information from the configuration system at
<subsystem>.<servername>
where the servername is taken from the wrapper
configuration.
Here is a complete sample configuration (found in scep/generic.yaml). The sections token, workflow and response are only used by SCEP, the export_certificate is only applicable to the RPC output. The remainder of the configuration is the same for all subsystems:
# By default, all scep endpoints wll use the default token defined
# by the scep token group, if you pass a name here, it is considered
# a group name from the alias table
#token: ca-one-special-scep
workflow:
type: certificate_enroll
param:
# key: name in workflow context, value: parameter from scep wrapper
# server and interface are always set, the mapping below is
# the default set that is used when no map is given
transaction_id: transaction_id
signer_cert: signer_cert
pkcs10: pkcs10
_url_params: url_params
#_pkcs7: pkcs7
response:
# The scep standard is a bit unclear if the root should be in the chain
# or not. We consider it a security risk (trust should be always set
# by hand) but as most clients seem to expect it, we include the root
# by default.
# The getca response contains the certificate of the SCEP server itself
# and of the current active issuer (which can but need not to be the same!)
# You can define weather to have only the certificate itself (endentity),
# the chain without the root (chain) or the chain including the root
# (fullchain).
# Note: The response is cached internally in the datapool so changes
# will not show up immediately - to list the cached items use
# openxpkicli list_data_pool_entries --arg namespace=scep.cache.getca
# You can delete by setting the empty string as value with
# set_data_pool_entry (value="" force=1)
getca:
ra: fullchain
issuer: fullchain
# A renewal request is only accpeted if the used certificate will
# expire within this period of time.
renewal_period: 000060
# If the request was a replacement, optionally revoke the replaced
# certificate after a grace period
revoke_on_replace:
reason_code: keyCompromise
delay_revocation_time: +000014
authorized_signer:
rule1:
# Full DN
subject: CN=.+:pkiclient,.*
rule2:
# Full DN
subject: CN=my.scep.enroller.com:generic,.*
policy:
# Authentication Options
# Initial requests need ONE authentication.
# Activate Challenge Password and/or HMAC by setting the appropriate
# options below.
# if set requests can be authenticated by an operator
allow_man_authen: 1
# if set, no authentication is required at all and hmac/challenge is
# not evaluated even if it is set/present in the request!
allow_anon_enroll: 0
# Approval
# If not autoapproved, allow opeerator to add approval by hand
allow_man_approv: 1
# if the eligibiliyt check failed the first time
# show a button to run a recheck (Workflow goes to PENDING)
allow_eligibility_recheck: 0
# Approval points requirede (eligibity and operator count as one point each)
# if you set this to "0", all authenticated requests are auto-approved!
approval_points: 1
# The number of active certs with the same subject that are allowed
# to exist at the same time, deducted by one if a renewal is seen
# set to 0 if you dont want to check for duplicates at all
max_active_certs: 1
# option will be removed
# allow_expired_signer: 0
# If an initial enrollment is seen
# all existing certificates with the same subject are revoked
auto_revoke_existing_certs: 1
# allows a "renewal" outside the renewal window, the notafter date
# is aligned to the old certificate. Set revoke_on_replace option
# to revoke the replaced certificate.
# This substitutes the "replace_window" from the OpenXPKI v1 config
allow_replace: 1
# by default only the certificate identifier is written to the workflow
# set to a true value to get the PEM encoded certificate in the context,
# set to "chain" to get the issuer certificate and "fullchain" to get
# the chain including the root certificate (key chain).
export_certificate: chain
profile:
cert_profile: tls_server
cert_subject_style: enroll
# Mapping of names to OpenXPKI profiles to be used with the
# Microsoft Certificate Template Name Ext. (1.3.6.1.4.1.311.20.2)
profile_map:
pc-client: tls_client
# HMAC based authentication
hmac: verysecret
# see below how to get a per-request password
challenge:
value: SecretChallenge
eligible:
initial:
value@: connector:scep.generic.connector.initial
args: '[% context.cert_subject_parts.CN.0 %]'
expect:
- Build
- New
renewal:
value: 1
connector:
initial:
class: Connector::Proxy::YAML
# this file must have a key/value list with the key being
# the subject and the value being a true value
# e.g. "pc1234.example.org: 1"
LOCATION: /home/pkiadm/cmdb.yaml
The renewal period values are interpreted as OpenXPKI::DateTime relative date but given without sign.
Upgrade from OpenXPKI v1 enrollment workflow¶
If you are upgrading from OpenXPKI 1.x enrollment workflow to the new one, you must adjust several parameters in the scep server configuration.
renewal/replace period
The logic for replace has changed, replace is now always assumed when you are outside the renewal period:
# old syntax
renewal_period: 000014
replace_period: 05
# new syntax
renewal_period: 000014
# note that the policy node already exists!
policy:
allow_replace: 1
signer on behalf
The name of the key has changed from authorized_signer_on_behalf to authorized_signer only:
# old syntax
authorized_signer_on_behalf:
rule1:
......
# new syntax
authorized_signer:
rule1:
......
profile definition
In OpenXPKI 1.0 the default profile was set in the CGI wrapper configuration. This has been moved to a seperate node in the endpoint configuration:
profile:
cert_profile: tls_server
cert_subject_style: enroll
key_checks
Are now read from the profiles, so there is no longer an extra definition in the workflow.
Workflow Configuration¶
Test-Drive (INSECURE)¶
If you need a server that just creates certificates, use the following policy section:
policy:
allow_anon_enroll: 1
approval_points: 0
max_active_certs: 0
allow_replace: 0
export_certificate: chain
This will issue any certificate for any request - so do not use this in production
Authentication¶
Signer on Behalf¶
The section authorized_signer is used to define the certificates which are accepted to do a “request on behalf”. The list is given as a hash of hashes, were each entry is a combination of one or more matching rules.
Possible rules are subject, profile and identifier which can be used in any combination. The subject is evaluated as a regexp against the signer subject, therefore any characters with a special meaning in perl regexp need to be escaped! Identifier and profile are matched as is. The rules in one entry are ANDed together. If you want to provide alternatives, add multiple list items. The name of the rule is just used for logging purpose.
Challenge Password¶
The request must carry the password in the challengePassword attribute. The sample config above shows a static password example but it is also possible to use request parameters to lookup a password using connectors:
challenge:
mode: bind
value@: connector:scep.connectors.challenge
args:
- "[% context.cert_subject %]"
connectors:
challenge:
class: Connector::Builtin::Authentication::Password
LOCATION: /home/pkiadm/democa/passwd.txt
This will use the cert_subject to validate the given password against a list found in the file /home/pkiadm/democa/passwd.txt. For more details, check the man page of OpenXPKI::Server::Workflow::Activity::Tools::ValidateChallengePassword
Renewal/Replace¶
A request is considered to be a renewal if the request is not self-signed but the signer subject matches the request subject. Renewal requests pass authentication if the signer certificate is valid in the current realm and neither revoked nor expired. You can allow expired certificates by setting renewal.notafter (Not implemented yet!).
Manual Authentication¶
If you set the allow_man_authen policy flag, request that fail any of the above authentication methods can be manually authenticated via the UI.
No Authentication¶
To completly skip authentication, set allow_anon_enroll policy flag.
Subject Checking¶
The policy setting max_active_certs gives the maximum allowed number of valid certificates sharing the same subject. If the certificate count after issuance of the current request will exceed this number, the workflow stops in the PENDING_POLICY_VIOLATION state. If this parameter is not set, no checks are done. There are several settings that influence this check, based on the operation mode:
Initial Enrollment¶
If you set the auto_revoke_existing_certs policy flag, all certificates with the same subject will be revoked prior to running this check. This does not make much sense with max_active_certs larger than 1 as all certificates will be revoked as soon as a new enrollment is started! The intended use is replacement of broken systems where the current certificate is no longer used anyway.
Renewal/Replace¶
If the request is a renewal or replacement request, it is allowed to exceed the max_active_certs by one.
Eligibility¶
The default config has a static value of 1 for renewals and 0 for initial requests. If you set approval_points to 1, this will result in an immediate issue of certificate renewal requests but requires operator approval on initial enrollments.
Assume you want to use an ldap directory to auto approve initial requests based on the mac address of your client:
eligible:
initial:
value@: connector:your.connector
args:
- "[% context.cert_subject %]"
- "[% context.url_mac %]"
connectors:
devices:
## This connector just checks if the given mac
## exisits in the ldap
class: Connector::Proxy::Net::LDAP::Simple
LOCATION: ldap://localhost:389
base: ou=devices,dc=mycompany,dc=com
filter: (macaddress=[% ARGS.1 %])
binddn: cn=admin,dc=mycompany,dc=com
password: admin
attrs: macaddress
To have the mac in the workflow, you need to pass it with the request as an url parameter to the wrapper: http://host/scep/scep?mac=001122334455.
For more options and samples, see the perldoc of OpenXPKI::Server::Workflow::Activity::Tools::EvaluateEligibility
Approval¶
A request is approved if it reaches the number of approvals defined by the approval_points policy setting. As written above, you can use a data source to get one approval point via the eligibility check. If a request has an insufficient number of approvals, the workflow will stop and an operator must give an approval using the WebUI. By raising the approval points value, you can also enforce a four-eyes approval. If you do not want manual approvals, set the policy flag allow_man_approv to zero - all requests that fail the eligibility check will be immediately rejected.
Certificate Configuration¶
SCEP Server Token¶
This is the cryptographic token used to sign and decrypt the SCEP communication itself. It is not related to the issuing process of the requested certificates!
The crypto configuration of a realm (crypto.yaml) defines a default token to be used for all scep services inside this realm. In case you want different servers to use different certificates, you can add additional token groups and reference them from the config using the token key.
The value must be the name of a token group, which needs to be registered as an anonymous alias:
openxpkiadm alias --realm democa --identifier <identifier> --group democa-special-scep --gen 1
Note that you need to care yourself about the generation index. The token will then be listed as anonymous group item:
openxpkiadm alias --realm democa
=== anonymous groups ===
democa-special-scep:
Alias : democa-special-scep-1
Identifier: O9vtjge0wHpYhDpfko2O6xYtCWw
NotBefore : 2014-03-25 15:26:18
NotAfter : 2015-03-25 15:26:18
Profile Selection / Certificate Template Name Extension¶
This feature was originally introduced by Microsoft and uses a Microsoft specific OID (1.3.6.1.4.1.311.20.2). If your request contains this OID and the value of this oid is listed in the profile map, the workflow will use the given profile definition to issue the certificate. If no OID is present or the value is not in the map, the default profile from the server configuration is used. This map is also used if the you pass the profile as parameter in an RPC call.
The map is a hash list:
profile_map:
tlsv2: tls_server_v2
client: tls_client
Subject Rendering¶
Subject rendering is based on the profile and subject information given in the config:
profile:
cert_profile: tls_server
cert_subject_style: enroll
The subject will be created using Template Toolkit with the parsed subject hash as input vars. The vars hash will use the name of the attribute as key and pass all values as array in order of appearance (it is always an array, even if the attribute is found only once!). You can also add SAN items but there is no way to filter or remove san items that are passed with the request, yet.
Example: The default TLS Server profile contains an enrollment section:
enroll:
subject:
dn: CN=[% CN.0 %],DC=Test Deployment,DC=OpenXPKI,DC=org
The issued certificate will have the common name extracted from the incoming request but get the remaining path compontens as defined in the profile.
Revoke on Replace¶
If you have a replace request (signed renewal with signer validity outside the renewal window), you can trigger the automatic revocation of the signer certificate. Setting a reason code is mandatory, supported values can be taken from the openssl man page (mind the CamelCasing), the delay_revocation_time is optional and can be relative or absolute date as consumed by OpenXPKI::DateTime, any empty value becomes “now”:
revoke_on_replace:
reason_code: superseded
delay_revocation_time: +000002
The above gives your friendly admins a 48h window to replace the certificates before they show up on the next CRL.
Note: Without any other measures, this will obviously enable an attacker who has access to a leaked key to obtain a new certificate. We used this to replace certificates after the Heartbleed bug with the scep systems seperated from the public network.
WebUI Page API Reference¶
The web pages are created (mainly) on the client from a JSON control stucture delivered by the server. This document describes the structure expected by the rendering engine.
Top-Level Structure¶
This is the root element of any json result:
%structure = (
page => { TOP_LEVEL_INFO },
right => [ PAGE_SECTION, PAGE_SECTION,...] , # optional, information which will be displayed in additional right pane
main => [ PAGE_SECTION, PAGE_SECTION,...] , # information which will be displayed in the main section
reloadTree => BOOL (1/0), # optional, the browser will perform a complete reload. If an additional "goto" is set, the page-url will change to this target
goto => STRING PAGE, # optional, will be evaluated as url-hashtag target
status => { STATUS_INFO } # optional
);
Example { reloadTree => 1, goto => 'login/login' }
Page Head (TOP_LEVEL_INFO):¶
This is rendered as the page main headline and intro text.
TOP_LEVEL_INFO:
{
label => STRING, #Page Header
description => STRING, # additional text (opt.)
}
Example:
page => { label => 'OpenXPKI Login', description => 'Please log in!' }
Status Notification (STATUS_INFO):¶
Show a status bar on top of the page, the level indicates the severity and results in different colors of the status bar.
STATUS_INFO:
{
level => STRING, # allowed values: "info", "success","warn", "error"
message => STRING # status message shown
}
Example:
status => { level => 'error', message => 'Login credentials are wrong!' }
Page Level¶
The page sections (main
and right
) can hold multiple subpage definitions. The main section must always contain at least one section while right can be omitted or empty.
Page Section (PAGE_SECTION)¶
This is the top level container of each page section.
PAGE_SECTION:
{
type => STRING # see SECTION-TYPE below for supported types
content => {
label => STRING # optional, section headline
description => STRING , # optional, additional text (html is allowed)
buttons => [ BUTTON_DEF, BUTTON_DEF, ... ] , # optional, defines the buttons/links for this section
# additional content-params depending on type (see below)
},
# additional section-params depending on type:
}
SECTION-TYPE “text”¶
Print the label as subheadline (h2) and description as intro text, buttons are rendered after the text. Does not have any additional parameters. Note: If you omit label and description this can be used to render a plain button bar or even a single button.
SECTION-TYPE “grid”¶
Grids are rendered using the jquery datatable plugin (http://datatables.net). The grid related parameters are just pushed to the dataTables engine and therefore have a different notation and syntax used as the remainder of the project.
content => {
label => ..,
description => ..,
buttons => [ BUTTON_DEF, BUTTON_DEF, ... ] , # optional, defines the buttons/links for this grid
columns => [ GRID_COL_DEF, GRID_COL_DEF , GRID_COL_DEF... ],
data => [ GRID_ROW, GRID_ROW, GRID_ROW, ... ],
actions => [ GRID_ACTION_DEF, GRID_ACTION_DEF, GRID_ACTION_DEF... ], # defines available actions, displayed as context menu
processing_type => STRING, # only possible value (for now) is "all"
}
GRID_COL_DEF:
{
sTitle => STRING, # displayed title of that columnd AND unique key
format => STRING_FORMAT # optional, triggers a formatting helper (see below)
}
GRID_ROW:
['col1','col2','col3']
GRID_ACTION_DEF:
{
page => STRING_PATH, # calls an OpenXPKI page. Terms enclosed in {brackets} will be evaluated as column-keys and replaced with the value of the given row for that column
action => STRING_PATH, # calls an OpenXPKI action. Replacements work as in "page".
href => STRING_PATH, # opens a webpage. Replacements work as in "page".
label => STRING, # visible menu entry
target => STRING_TARGET # optional, where to open the new page, one of self|top|popup
icon => STRING , # optional, file name of image icon, must be placed in htdocs/img/contextmenu
}
Columns, whose sTitle begin with an underscore will not be displayed but used as internal information (e.g. as path in GRID_ACTION_DEF). A column with the special title _status
is used as css class for the row. Also a pulldown menu to filter by status will be displayed.
The rows hold the data in form of a positional array.
Action target popup
creates a modal popup.
Example:
content => {
columns => [
{ sTitle => "Serial" },
{ sTitle => "Subject" },
{ sTitle => "date_issued", format => 'timestamp'},
{ sTitle => "link", format => 'link'},
{ sTitle => "_id"}, # internal ID (will not be displayed)
{ sTitle => "_status"}, # row status
],
data => [
['0123','CN=John M Miller,DC=My Company,DC=com',1379587708, {page => 'http://../', label => 'Click On Me'}, 'swBdX','issued'],
['0456','CN=Bob Builder,DC=My Company,DC=com',1379587517,{...},'qqA2H','expired'],
],
actions => [
{
page => 'cert!detail!{_id}',
label => 'Details',
icon => 'view',
target => 'popup'
},
{
page => 'cert!mail2issuer!{email}',
label => 'Send an email to issuer'
},
]
}
SECTION-TYPE “form”¶
Render a form to submit data to the server
content => {
label => STRING,
description => STRING,
buttons => [ BUTTON_DEF, BUTTON_DEF, ... ], # a form must contain at least one button to be useful
fields => [ FORM_FIELD_DEF, FORM_FIELD_DEF, ... ],
}
FORM_FIELD_DEF:
{
name => STRING # internal key - will be transmitted to server
value => MIXED, # value of the field, scalar or array (depending on type)
label => STRING, # displayed label
type => STRING_FIELD_TYPE, # see FIELD-TYPE below for supported types
is_optional => BOOL, # if false (or not given at all) the field is required
clonable => BOOL, # creates fields that can be added more than once
visible => BOOL, # if set to "false" ("0" in perl) this field will be not displayed (initial)
keys => ARRAY, # optional, activates the special feature of "dynamic key value fields", see below.
# + additional keys depending for some types
}
FIELD-TYPE “static”¶
No additional parameters, creates a simple “readonly” text element with the value treated as a “hidden” form element. If you want to display a formatted version of the value instead, you can pass it using the verbose key.
FIELD-TYPE “checkbox/bool”¶
A html checkbox; value and is_optional are without effect, as always 0 or 1 is send to the server.
FIELD-TYPE “date”¶
A text field with a jquery datapicker attached. Additional (all optional) params are:
FORM_FIELD_DEF:
{
notbefore => INTEGER, # optional, unixtime, earliest selectable date
notafter => INTEGER, # optional, unixtime, earliest selectable date
return_format => STRING # one of terse|printable|iso8601|epoch, see OpenXPKI::Datetime
}
FIELD-TYPE “select”¶
A html select element, the options parameter is required, others are optional:
FORM_FIELD_DEF:
{
options => [{value=>'key 1',label=>'Label 1'},{value=>'key 2',label=>'Label 2'},...],
prompt => STRING # first option shown in the box, no value (soemthing like "please choose")
editable => BOOL # activates the ComboBox,
actionOnChange => STRING_ACTION # if the pulldown is changed by the user (or an initial value is given), server will be called with this "action". See "Dynamic form rendering" for details.
}
The options
parameter can be fetched via an ajax call. If you set options => 'fetch_cert_status_options'
, an ajax call to “server_url.cgi?action=fetch_cert_status_options” is made. The call must return the label/value list as defined given above.
Setting the editable flag to a true value enables the users to enter any value into the select box (created with Bootstrap Combobox).
FIELD-TYPE “radio”¶
The radio type is the little brother of the select field, but renders the items as a list of items using html radio-buttons. It shares the syntax of the options
field with the select element:
FORM_FIELD_DEF:
{
options => [{....}] or 'ajax_action_string'..
multi => BOOL, # optional, if true, uses checkbox elements instead radio buttons
}
FIELD-TYPE “upload”¶
Renders a field to upload files with some additional benefits:
FORM_FIELD_DEF:
{
mode => STRING, # one of hidden, visible, raw
allowedFiles => ARRAY OF STRING, # ['txt', 'jpg'],
textAreaSize => {width => '10', height => '15'},
}
By default, a file upload button is shown which loads the selected file into a hidden textarea. Binary content is encoded with base64 and prefixed with the word “binary:”. With mode = visible the textarea is also shown so the user can either upload or paste the data (which is very handy for CSR uploads), the textAreaSize will affect the size of the area field. With mode = raw
the element degrades to a html form upload button and the selected file is send with the form as raw data.
AllowedFiles can contain a list of allowed file extensions.
Dynamic key value fields¶
If a field is defined with the property “keys”, a pulldown of options is displayed above the actual field. This allows the user to specify, which kind of information he wants to specify. The content of the actual field will be submitted to the server with the selected key in the key-pulldown.
Example:
{ name => '...', label => 'Dyn Key-Value', 'keys' => [{value=>"key_x",label=>"Typ X"},{value=>"key_y",label=>"Typ Y"}], type => 'text' },
This example definition will render a Textfield with label “Dyn Key-Value”. Above the textfield a select is displayed with three options (“Typ x”,”Typ y” and “Typ z”). If the user chooses “Typ Z”, the entered value in the textfield will be posted to server with key “key_z”.
This feature makes often more sense in combination with “clonable” fields.
Dynamic form rendering¶
If a select field is defined with the property “actionOnChange”, each change event of this pulldown will trigger an submit of all formvalues (without validity checks etc) to the server with key “action” set to the value of “actionOnChange”.
Partial redefinition:
The key “fields” is expected in the returned JSON-Structure. “fields” contains an array, which is semantically identic to the key “fields” in the definition of the form. This array “fields” must contain only only the fields (and properties), which should react to the change of the (master-)field (pulldown) . The property “name” is required (otherwise the client can not identify the field). The property “type” can not be subject to changes. With aid of the property “visible” one can dynamically show or hide some fields. Only known fields (which are already defined in the initial “fields”-property of the form-section) can be subject of the “partial” re-rendering. Its not possible to add new fields here.
You can define more than one (cascading) dependent select.
Example:
Initial definition of fields:
fields => [
{ name => 'cert_typ', label => 'Typ',value=> 't2', prompt => 'please select a type', type => 'select', actionOnChange => 'test_dep_select!change_type', options => [{value=>'t1',label=>'Typ 1'},{value=>'t2',label=>'Typ 2'},{value=>'t3',label=>'Typ 3'}] },
{ name => 'cert_subtyp', label => 'Sub-Type', prompt => 'first select type!', type => 'select', options => [] },
{ name => 'special', label => 'Special (only type 2)', type => 'checkbox', visible => 0 },
]
Action “test_dep_select!change_type” returns a (partially updated) definition of fields:
{
fields => [
{ name => 'cert_subtype', options => [{value=>'x', label => 'Subtype X'}, ...], value => 'x' } ,
{ name => 'special', visible=> 1 }
]
};
Full redefinition:
Is not implemented yet.
SECTION-TYPE “key-value”¶
Render a list of key/value items in a two column grid. The left column shows the text given by label, the right column is formated based on value and format (see Formatted Strings).
There is a special format type head
which renders a table head tag spanning
both columns. If a context item is referenced, value is used as headline,
it might be decorated using a template. As an alternative, a fixed value can
be given using the key label.
Add an item with format set to spacer
to create an empty separator line
(there is a global workflow field named spacer in the default ca-one config
so you can just say - spacer
in the workflow output
section).
An option className can be set which is put into the rows’ <tr>
tag.
Item Level¶
Buttons (BUTTON_DEF)¶
Defines a button. There are three modes, depending on which one of these parameters is specified: page, action, href.
Common parameters for page and action:
{
label => STRING, # The label of the button
tooltip => STRING, # (optional)
className => STRING, # CSS class (optional)
confirm => {
label => STRING, #
description => STRING, #
confirm_label => STRING, # (optional, defaults to "Confirm")
cancel_label => STRING, # (optional, defaults to "Abort")
},
}
target: determines where the contents returned by the server shall be displayed:
main
- as main tab (close all other tabs)popup
- as a modal popuptab
- in a new tabactive
- in the active tab
page: load a page (GET request, no client-side parameters)
- ::
- {
- page => STRING, # page to render (GET request with parameter “page”) # + Common parameters (see above)
}
This calls an init_*
method in the specified class.
action: execute action via AJAX (POST request with client-side parameters)
- ::
- {
- action => STRING, # action to execute (POST request with parameter “action”) # + Common parameters (see above)
}
This calls an action_*
method in the specified class.
href: open a custom URL:
{
href => STRING, # URL to call
target => STRING, # any HTML link target, e.g. '_blank'
label => STRING, # The label of the button
tooltip => STRING, # (optional)
className => STRING, # CSS class (optional)
}
Formatted Strings (STRING_FORMAT)¶
Tells the UI to process the data with a special formatter before rendering. Available methods are:
timestamp¶
Expects a unix timestamp and outputs a full UTC timestamp.
datetime¶
Expects a parseable date, outputs a full UTC timestamp.
certstatus¶
Colorizes the given status word using css tags:
{
label => STRING, # text to show
value => STRING, # used alternatively to assemble CSS class (optional)
tooltip => STRING, # (optional)
}
The CSS class is assembled as follows: certstatus-
+ value. If no value is given: certstatus-
+ label.
E.g. label => "issued"
becomes:
<span class="certstatus-issued">issued</span>
link¶
Create an internal framework link to a page or action, expects a hash like:
{
label => STRING, #
page => STRING, #
target => [self|top|popup], # (optional, defaults to "top")
tooltip => STRING, # (optional)
}
extlink¶
Similar to link but: expects href to be an external target, default target is _blank
.
text¶
Readable text without html markup (special characters will be escaped)
nl2br¶
Like text but with line breaks \n
converted to <br>
raw¶
Displays the given text as is (i.e. HTML formatting allowed).
code¶
Rendered with fixed-with typo, unix linebreaks are converted to html linebreaks.
deflist¶
Outputs a key/value list (dl/dt/dd) - expects an array where each item is a hash with keys key and value.
ullist¶
Array of values, each item becomes a <li> in the list, values are html-escaped.
rawlist¶
Like ullist but displays the items “as is” (can contain HTML markup)
linklist¶
Array, where each item is a hash describing a link
styled¶
Expects a value in the format stylename:Text to display
. The part left
of the colon is extracted and the text at the right is wrapped with span
with style class “styled-stylename
. Predefined stylenames are
valid
, failed
and attention
tooltip¶
Shows the given text as is (i.e. HTML formatting allowed) and adds a tooltip to it:
{
value => STRING, # text to be shown
tooltip => STRING, # text of the tooltip
}
Customization¶
The framework allows to register additional components via an exposed api.
Form-Field¶
Add a new FormField-Type:
OXI.FormFieldFactory.registerComponent('type','ComponentName',JS_CODE [,bOverwriteExisting]);
Example:
OXI.FormFieldFactory.registerComponent('select','MySpecialSelect', OXI.FormFieldContainer.extend({
....
}), true);
This will overwrite the handler for the select element. The ComponentName will be registered in the OXI Namespace and can be used to call the object from within userdefined code.
Formatter¶
Add a new Format-Handler:
OXI.FormatHelperFactory.registerComponent('format','ComponentName',JS_CODE [,bOverwriteExisting])
Automation / External APIs
Subsystems providing External APIs¶
OpenXPKI comes with a set of subsystems that can be used to search for certificates and handle workflows using different established or custom APIs.
The most common one is the SCEP interface that supports enrollment of certificates using the “Simple Certificate Enrollment Protocol” which was developed by Cisco and is widely used in hardware devices.
A generic SOAP and RPC wrapper is also available which can be used to implement arbitrary workflows. Some samples are already included in the default config.
If you want connect your Microsoft Windows environment to OpenXPKI, have a look on the “Certificate Enrollment Proxy” (https://www.secardeo.de/produkte/certep/). This commercial product is a snap-in that routes certificate requests from a Microsoft CA to OpenXPKI, enabling approval workflows and certificate lifecycle management for Windows environments.
Wrapper Configuration¶
All wrappers are implemented as a fast-cgi script with the default
webserver handling the HTTP layer, talking to the OpenXPKI daemon using
the existings socket. The basic configuration pattern is the same for
all subsystems, just replace the rpc
used in the given samples with
the name of the wrapper.
Each wrapper has a directory of the same name in the openxpki main config folder (e.g. /etc/openxpki/rpc) holding the global default and the logger config at least. Currently, only the Config::Std format is supported for those files!
Global Default Config¶
The name of the global config file must be default.conf
and consists
of three section holding information on logger, auth and socket:
[global]
socket = /var/openxpki/openxpki.socket
[logger]
log_level = WARN
[auth]
stack = _System
socket Full path of the OpenXPKI socket file
log_level A Log4perl log level, the logger will be auto created to use a Log4perl Appender with a logfile at /var/log/openxpki/<servicename>.log.
stack Name of the authentication stack, default is to connect as Anonymous, all additional attributes are passed unaltered to the authentication layer. See OpenXPKI::Client::Simple.
Alternative logger configuration (default before v3.22)¶
If you want to have more control over the logger, omit the logger section, create a Log4perl configuration file and add it in the I<global> section:
[global] ….other setting…. log_config = /etc/openxpki/rpc/log.conf log_facility = client.rpc
log_config must point to the Log4perl configuration file that should be used by this wrapper. If the file is not found or the option is missing, a default logger writing to STDERR is used.
log_facility The facility name to log with, this is useful if you want to log to the same file from multiple different systems.
See OpenXPKI::Client:Config for more details on logger configuration.
Config Path Expansion¶
If you run the cgi wrapper scripts using the provided Alias rules, you can
have multiple named configurations. Call the wrapper using an alias
path like http://host/rpc/vpnclient
to load the config information from
/etc/openxpki/rpc/vpnclient.conf
.
If no file is found, the default config is loaded from /etc/openxpki/scep/default.conf. The wrapper uses SCRIPT_URL or REQUEST_URI from the apache environment to extract the requests path, this should work in most environments with mod_rewrite, symlinks or alias definitions. If you use another webserver then apache, you might need to adjust the autodetection rules to fit your needs.
Note The wrappers run as persistent scripts and are initialized before the alias path is known. The socket and logger config is therefore always read from the default.conf!
custom base directory¶
The value of OPENXPKI_CLIENT_CONF_DIR
overwrites the default of the top
level configuration folder /etc/openxpki
. If set, the service is config is
loaded from OPENXPKI_CLIENT_CONF_DIR/<servicename>
.
custom service directory¶
Set OPENXPKI_RPC_CLIENT_CONF_DIR
to a directory path. The autodetection
will now use this path to find either the special or the default file. Note
that there is no fallback to the default location!
fixed file¶
Set OPENXPKI_RPC_CLIENT_CONF_FILE
to an absolute file path. On apache,
this can be combined with location to set a config for a special script:
<Location /cgi-bin/rpc/mailgateway>
SetEnv OPENXPKI_SCEP_CLIENT_CONF_FILE /home/mailadm/rpc.conf
</Location>
Log4perl Config¶
The default Log4perl config shipped with the sample config file looks like:
log4perl.category.client.rpc = INFO, Logfile
log4perl.appender.Logfile = Log::Log4perl::Appender::File
log4perl.appender.Logfile.filename = /var/log/openxpki/rpc.log
log4perl.appender.Logfile.layout = Log::Log4perl::Layout::PatternLayout
log4perl.appender.Logfile.layout.ConversionPattern = %d %p:%P %m%n
log4perl.appender.Logfile.syswrite = 1
Note: The wrappers run in the context and with permissions of the webserver! You need to make sure that the directory or preexisting files have appropriate permission to be written/created by this user/group!
Webserver Config¶
Config Path Expansion¶
The most convenient way to enable the path expansion is to use the Alias
directive:
# Same for RPC
ScriptAlias /rpc /usr/lib/cgi-bin/rpc.fcgi
<Directory "/usr/lib/cgi-bin/">
AllowOverride None
Options +ExecCGI
Order allow,deny
Allow from all
# Remove this line if you are using apache 2.2
Require all granted
</Directory>
TLS Client Authentication¶
All wrappers except SCEP support authentication using TLS client certificates. The recommended way is to let apache do the TLS handshake but pass the full client certificate to OpenXPKI:
SSLVerifyClient optional
SSLVerifyDepth 3
SSLCACertificateFile /etc/apache2/ssl/root.pem
SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire
<Directory /usr/lib/cgi-bin>
SSLOptions +StdEnvVars
</Directory>
This makes the properties and the full certificate as PEM available in the SSL_* environment variables where there are picked up as needed and injected into the workflow engine by the wrappers.
Endpoint Configuration¶
Most of the workflows used with the external APIs use a common pattern to load endpoint specific settings. The interface type together with the servername is used as base path for config lookups. Note that the servername is given explicit in the wrapper config and can be different from the exposed script name.
A sample RPC endpoint configuration might look like:
[RequestCertificate]
workflow = certificate_enroll
param = pkcs10, comment
output = cert_identifier, error_code
env = signer_cert
servername = vpnclient
The base path for config lookups is now, inside the realm config
rpc.vpnclient
.
If you want to deploy multiple endpoints with the same configuration you can also replace the explicit servername = vpnclient with env = server which will set the internal servername to the name of the endpoint. When you put your configuration in default.yaml this ends up in an automatic factory where each URI …/rpc/anyname/Method will result in a valid call to the workflow using anyname as base for the configuration lookup.
TLS Client Authorization¶
A widely used example is the check if a client is authorized to run the
workflow based on the provided TLS certificate. Most of the workflows use the
OpenXPKI::Server::Workflow::Activity::Tools::EvaluateSignerTrust
action class for this which grabs the ruleset from
interface.servername.authorized_signer
, in our example
rpc.vpnclient.authorized_signer
:
authorized_signer:
rule1:
subject: CN=.+:pkiclient,.*
rule2:
profile: vpn_client
realm: vpn-ca
rule3:
identifier: AhElV5GzgFhKalmF_yQq-b1TnWg
The provided certificate is matched against each rule, the check returns true if all conditions of one rule are met. The realm is always set to the current realm if not given explicit. The subject is matched as case-insensitive regex all other attributes are matched as equal strings.
RPC Server API¶
The RPC Service provides a simple HTTP-Based API to the OpenXPKI backend. The builtin REST Server provides methods to request, renew and revoke certificates. The service is implemented using a cgi-wrapper script.
Server-Side Configuration¶
Wrapper Configuration¶
The default wrapper looks for its config file at /etc/openxpki/rpc/default.conf
.
The config uses plain ini format, a default is deployed by the package:
[global]
socket = /var/openxpki/openxpki.socket
locale_directory: /usr/share/locale
default_language: en_US
[logger]
log_level = WARN
[auth]
stack = _System
[input]
allow_raw_post = 1
parse_depth = 5
[output]
use_http_status_codes=0
The global/auth/logger parameters are described in the common wrapper documentation (Wrapper Configuration). Config path extension and TLS Authentication is supported.
TLS Authentication¶
In case you want to use TLS Client authentication you must tell the webserver to pass the client certificate to the script. For apache, put the following lines into your SSL Host section:
<Location /rpc>
SSLVerifyClient optional
SSLOptions +StdEnvVars +ExportCertData
</Location>
Note: We need the apache just to check if the client has access to the private key of the certificate. Trust and /revocation checking is done inside of OpenXPKI so you can also use “optional_no_ca” if you dont want to deal with setting up the correct chains in apache. Blocking clients on TLS level might be a good idea if your service is exposed to “unfriendly users”.
Input Handling¶
allow_raw_post¶
Adds the option to post the parameters as json string as raw http body. As we currently do NOT sanitize the parameters send there is a chnace for an attacker to inject serialized json objects this way! So do NOT set this until you are running in a trusted, controlled environemnt or have other security mechanisms in place.
parse_depth¶
Maximum allowed recursion depth for the JSON body, the default is 5.
Parameter Handling¶
Input parameters are expected to be passed either via query string or in the body of the HTTP POST operation (application/x-www-form-urlencoded).
At minimum, the parameter “method” must be provided. The name of the method used must match a section in the configuration file, which must at least contain the name of a workflow.
If you have setup config path expansion, you can append the method as third parameter to the URL instead:
http://demo.openxpki.org/rpc/helpdesk/RevokeCertificateByIdentifier
The default is to return JSON formatted data, if you set the I<Accept> header of your request to “text/plain”, you will get the result as plain text with each key/parameter pairs on a new line.
Example: Revoke Certificate by Certificate Identifier¶
The endpoint is configured in /etc/openxpki/rpc/enroll.conf
with
the following:
[RevokeCertificate]
workflow = certificate_revocation_request_v2
param = cert_identifier, reason_code, comment, invalidity_time
env = signer_cert, signer_dn, server
output = error_code
See core/server/cgi-bin/rpc.cgi
for mapping additional parameters,
if needed.
Certificates are revoked by specifying the certificate identifier:
curl \
--data "method=RevokeCertificateByIdentifier" \
--data "cert_identifier=3E9tpLu5qpXarcQHnD1LUNsJIpU" \
--data "reason_code=unspecified" \
http://demo.openxpki.org/cgi-bin/rpc.cgi
The response is in JSON format (http://www.jsonrpc.org/specification#response_object). Except for the “id” parameter, the result is identical to the definition of JSON RPC:
{ result: {
id: workflow_id, pid: process_id, state: workflow_state, proc_state: proc_state
}}
On error, the content returned is:
{ error: { code: 1, message: "Verbose error", data: { id, pid, state } } }
Verbose error might be a readable error message or a I18N… translatable tag. If you set default_language in the wrapper configuration the I18N tags are translated.
Response¶
By default, the HTTP Status code is always “200 ok” with a numeric error code set in the return structure. The error codes consist of five digits, the first three digits are derived from the HTTP status codes followed by two digits for unambiguousness.
To let the wrapper send the error code on HTTP layer, you need to set:
[output]
use_http_status_codes=1
in the wrapper configuration. This will return 4xx and 5xx status codes together with the above mentioned error structures as body.
For details on the supported error codes see the documentation of the rpc.fcgi wrapper script.
Note: The OpenAPI Spec does not yet return the HTTP status codes.
Workflow Pickup¶
If you have a workflow that does not return the final result immediately, you can define a search pattern to pickup existing workflows based on worflow_attributes:
[RequestCertificate]
workflow = certificate_enroll
param = pkcs10, comment
output = cert_identifier, error_code, transaction_id
env = signer_cert, enroll
pickup = transaction_id
With a properly prepared workflow, this allows you access an existing workflow based on the transaction_id. For now it is only possible to read existing workflows, there is no option to interact with them, yet.
Examples¶
The default.conf configuration file defines an endpoint SearchCertificate:
[SearchCertificate]
workflow = certificate_search
param = common_name
output = cert_identifier, notbefore, notafter, status
To utilize this endpoint the following curl command may be used:
$ curl -F "method=SearchCertificate" -F "common_name=test" http://localhost:8080/rpc
{"result":{"id":0,"data":{"notafter":"2019-04-19T05:21:58","notbefore":"2018-10-19T05:21:58", \
"status":"ISSUED","cert_identifier":"7Da0qfjirGl7PXlZYf9PFVqMJds"},"state":"SUCCESS","pid":915}}
The RequestCertificate endpoint (see above) may be used via:
$ curl -F method=RequestCertificate -F comment=test -F pkcs10="$(cat certreq.pem)" http://localhost:8080/rpc
{"result":{"id":"5119","state":"SUCCESS","data":{"cert_identifier":"60uHCnC3Uv9wZKjcCkmSHuBwuzU"},"pid":915}}
Of course proper authentication and authorization is required for the cerificate to be issued immediately. The required configuration parameters are documented in the scep workflow.
Retrieving the OpenAPI spec¶
There is a special RPC method openapi-spec:
$ curl -F "method=openapi-spec" http://localhost:8080/rpc
This will return an OpenAPI compliant specification of all possible OpenXPKI RPC method calls in JSON format.
For an OpenAPI overview please see OpenAPI (aka Swagger).
See Also¶
See also core/server/cgi-bin/rpc.cgi
.
OpenAPI (aka Swagger)¶
This is not really an own subsystem but it offers an auto-generated OpenAPI 3.0 compliant specification of the RPC interface.
To generate the OpenAPI spec according to your current RPC configuration see Retrieving the OpenAPI spec.
The info block of the specification is by default set to contain a generic title and inherits the version number from system.config.api (as obtained by the version API call). To provide your own values for the info block, add a section [openapi] to the RPC wrapper configuration and set the expected values:
[openapi]
title = Public Certificate Reqest API
description = Request, Renew and Revoke your Certificates her
version = 42.1
The data types of all relevant input/output parameters of those workflows exposed via RPC must be defined (in the workflow config) to be able to generate the OpenAPI spec. For details see OpenAPI specific field parameters.
EST Endpoint / RFC 7030¶
The default configuration comes with a preconfigured endpoint for the “Enrollment over Secure Transport” Protocol as defined in RFC 7030.
As defined by the protocol the URL is https://<your host>/.well-known/est/
,
the endpoint maps simple(re)enroll to the certificate_enroll
workflow in a
similar way as SCEP or RPC. The CACerts and CSRAttrs call is also supported
and backed by a workflow that sends sane defaults suitable for most purposes.
The wrapper does not support the FullCMC protocol.
The only thing you might need to adjust in the wrapper configuration is the name of the ca realm in case you have more than one. For further details please see the RPC and the common wrapper configuration sections.
The configuration for the default URL is done via the file
<myrealm>/est/default.yaml
, you can load another configuration by using a
calabel
which loads the policy from est/<calabel>.yaml
.
Default Configuration¶
The default configuration supports anonymous enrollment with manual approval via UI or automatic issuance using Enrollment on Behalf with a signer certificate.
Smoke Test¶
If you dont have an EST client at hand, you can easily use curl and openssl:
openssl req -keyout /dev/null -subj "/CN=test me" -nodes -new -newkey rsa:3072 -outform der -out - | base64 > test.pem
curl -v -H "Content-Type: application/pkcs10" --data @test.pem https://demo.openxpki.org/.well-known/est/simpleenroll
This should return 202 Request Pending - Retry Later (a3...e6c)
which indicates that the request was queued for approval. Log into the UI to approve the request
and rerun the same line to fetch your certificate. The value in the brackets is the transaction
id assigned to this request by the PKI which you can use to find / identify the correct workflow.
Authenticated Test¶
Use the UI to obtain a TLS Client certificate with the application name pkiclient
and add
it to the query using the curl options --key/--cert
. You should now get your certificate immediately:
openssl req -keyout /dev/null -subj "/CN=test me" -nodes -new -newkey rsa:3072 -outform der -out - | base64 > test.pem
curl -s -H "Content-Type: application/pkcs10" --data @test.pem \
--key estclient/deadbeaf.key.pem --cert estclient/deadbeaf.cert.pem \
https://demo.openxpki.org/.well-known/est/simpleenroll | base64 -d | openssl pkcs7 -inform der -print_certs
Note: The EST standard states that only the end-entity certificate must be included in the response. To get the chain certificates query the cacerts method of the endpoint:
curl -s https://demo.openxpki.org/.well-known/est/cacerts | base64 -d | openssl pkcs7 -inform der -print_certs
SCEP Server¶
The communication with your scep clients requires the deployment of a cgi wrapper script with your webserver. The script will parse the HTTP related parts and pass the data to the openxpki daemon and vice versa.
Decommission and Upgrade Notice¶
With v3.26 the old SCEP wrappers based on a dedicated service layer are
no longer supported. You need to remove the service related items from
system.server.service
, system.crypto.tokenapi
and point the
/scep
alias rules in the apache wrapper to the scepv3.fcgi
script.
You also need to update the wrapper configurations in the
/etc/openxpki/scep
folder and the workflow configurations in the
realms.
Wrapper Configuration¶
The default wrapper looks for its config file at /etc/openxpki/scep/default.conf
.
The config uses plain ini format, a default is deployed by the package:
[global]
socket=/var/openxpki/openxpki.socket
realm=democa
servername=generic
[logger]
# A loglevel of DEBUG MIGHT disclose sensitive user input data
# A loglevel of TRACE WILL dump any communication unfiltered
log_level = INFO
[auth]
stack=_System
# OpenXPKI supports mapping additional URL Parameters to the workflow
# Those must be whitelisted here for security reasons
[PKIOperation]
param = signature
Config Path Expansion¶
Is supported by the SCEP wrapper, the service name is scep
. See the
common wrapper documentation (Wrapper Configuration) for details.
Caveats¶
The scep standard is not exact about the use of HTTP/1.1 features. We saw a lot of clients which where sending plain HTTP/1.0 requests which is not compatible with name based virtual hosting!
Please do NOT use SCEP over HTTPS, SCEP transport is protected on the application layer by default.
SOAP Server¶
The builtin SOAP Server provides methods to revoke certificates. The service is implemented using a cgi-wrapper script, so there is no need for the webserver to support SOAP, you just need to setup the wrapper script. For apache, just add a ScriptAlias:
ScriptAlias /soap /usr/lib/cgi-bin/soap.fcgi
Wrapper Configuration¶
The default wrapper looks for its config file at /etc/openxpki/scep/default.conf
.
The config uses plain ini format, a default is deployed by the package:
[global]
socket = /var/openxpki/openxpki.socket
modules = OpenXPKI::SOAP::Revoke OpenXPKI::SOAP::Smartcard
[logger]
log_level = WARN
[auth]
stack = _System
[OpenXPKI::SOAP::Revoke]
workflow = certificate_revocation_request_v2
servername = signed-revoke
[OpenXPKI::SOAP::Smartcard]
workflow = sc_revoke
servername = smartcard-revoke
The global/auth parameters are described in the common wrapper documentation (Wrapper Configuration). Config path extension is supported.
The modules
key must list the class names of
all modules that should be exposed. Most modules expect some extra
configuration. Put your parameters into a section with the name of the module,
those will be passed to the module when initialized.
Endpoint Configuration¶
Based on the given servername, a rules file is loaded for the server. You can define the rules for the signer authorization here:
authorized_signer:
rule1:
subject: CN=.+:soapclient,.*
rule2:
subject: CN=.+:pkiclient,.*
SOAP Methods¶
The default interface exposes two methods. The reason code is optional in both calls and defaults to “unspecified”. Allowed values are the reason codes as used by openssl.
RevokeCertificateByIssuerSerial¶
This expects the full DN of the certificate issuer and the serial number of the certificate to revoke. The serial can be either in decimal or hexadecimal format prefixed with ‘0x’:
RevokeCertificateByIssuerSerial(
'CN=CA ONE,OU=Test CA,DC=OpenXPKI,DC=ORG',
'0xdb7d5b06600bddcbecff',
'keyCompromise'
)
RevokeCertificateByIdentifier¶
Expects the OpenXPKI identifier of the certificate:
RevokeCertificateByIdentifier(
'TZNrDctI9RV8DT5TGvg81w7F-So',
'keyCompromise'
)
Both calls return a hash with id and state of the started workflow:
{
'id' => '145919',
'state' => 'PENDING',
'error' => ''
}
If anything goes wrong, you get a verbose error message in error:
{
'error' => 'parameter missing'
}
Operation
Audit Log¶
The audit log lists all operations that are relevant for the usage of private key material or important steps (as approvals) that lead to a signature using the CA key.
Categories¶
The audit log is divided into several categories. The given items are actions logged by the standard configuration but are not exhaustive. The name in brackets is the name of the logger category used by the logger.
CA Key Usage (cakey)¶
- certificate issued
- crl issued
Entity Key Usage (key)¶
- key generated
- key exported
- key destroyed
Certificate (entity)¶
- request received
- request fully approved
- issued
- revoked
Approval (approval)¶
- operator approval given via ui
- automated approval derived from backend checks
ACL (acl)¶
- access to workflow
- access to api
System (system)¶
- start/stop of system
- import/activation of tokens
- import of certificates
Application¶
- Application specific logging
Parameters¶
Each log message consists of a fixed string describing the event plus a list of normalized parameters which are appended as key/value pairs to the message, so it is easy to search the log for certain or feed it to a log analysis program like logstash.
- cakey/key: subject key identifier of the used key
- certid: certificate identifier
- wfid: id of the workflow
- action: name of a workflow action or called API method
- token: alias name of the token/key, e.g., “ca-signer-1”
- pki_realm: name of the pki realm
Example (line breaks are for verbosity, logfile is one line):
certificate signed|
cakey=28:B9:6D:51:EC:EB:6D:C9:4A:71:7C:B4:C0:67:F7:E9:C1:BD:63:7A|
certid=FW2Hq52uTcthhyhrrvTjRub66M0|
key=D6:14:BB:E2:90:12:F4:FF:64:B4:0F:F3:F6:3A:FD:17:02:C9:06:C8|
pki_realm=democa
Crypto Token Configuration¶
Overview¶
A cypto token is an entity used to do cryptographic operations. OpenXPKI organizes those tokens using groups and generations. A default system has four groups:
- certsign - represents the Issuing CA
- datasafe - used internally to encrypt sensitive data
- scep - the operational certificate of the SCEP server
- root - the root certificate of the Issuing CA chain
OpenXPKI expects that a token has only a limited lifetime and is substituted by a successor at a certain point in time. This relation is expressed by the generation counter.
Initial Setup¶
All tokens consist of a private key and a certificate, the certificate must be present in the OpenXPKI internal database and is referenced by the certificate identifier. The private key lives outside the OpenXPKI systems. When using the default config, the system expects the private key as file where the name of the file is constructed from the complete alias name.
Root Certificate¶
For production systems it is usual to have the Issuing CA under a Root CA and manage the Root CA on a offline system. As OpenXPKI needs the full chain of a certificate, you need to import the root certificate first:
openxpkiadm certificate import --file ca-root-1.crt
Issuing Certificate¶
After importing the root, or if you do not have a dedicated root, you can now import the issuing certificate:
openxpkiadm certificate import --file ca-signer-1.crt \
--realm democa --token certsign
This will import the certificate and also create a so called alias to mark this certificate as issuing token. With the default config, the key file is expected to be at /etc/openxpki/ca/democa/ca-signer-1.pem.
Datasafe Token¶
The datasafe token is represented by a certificate but is never exposed to the public so it is acceptable to use a self-signed certificate here:
openxpkiadm certificate import --file vault-1.crt \
--realm democa --token datasafe
The token is used for encrypting new items only as long as the certificate is valid. Expired tokens are still needed to decrypt existing items so never delete or overwrite them!
Token Rollover¶
If the lifetime of a token is approaching its end, you can just add a new token using the same commands as above. OpenXPKI will increase the internal generation counter and assign it to the new alias. Just make sure your key file has the correct name! If your token key are protected with a password, make sure that all passwords for all generations are still accessible as long as you need the token - issuing tokens are usually used to sign CRLs even after their active issuing period is over and datasafe tokens are required to access archived keys or other data.
Architecture
Developer
Tests¶
The OpenXPKI project contains a large and still growing number of tests to ensure code quality and ease refactoring.
Historically tests are categorized into two groups:
- unit tests in
core/server/t/
: tests for single classes and limited functionality that don’t need a running server or a complete configuration. - QA tests in
qatest/
: tests that need a running server or a more complete configuration.
Running tests¶
There are several methods to run all tests:
Using Docker¶
This method has minimal requirements for your host system.
Prerequisites: Docker
# assuming you are in the projects' root directory:
./tools/docker-test.pl --all
The script builds the Docker image locally which takes a while on first run.
Please note that this will only run tests on a MariaDB database. If you want to test Oracle connectivity, please use the Vagrant method below.
Using Vagrant¶
This method creates a complete interactive test environment inside a VirtualBox VM (i.e., “Vagrant Box”).
Prerequisites: Virtualbox, Vagrant, Oracle XE 11.2 setup
Once
Download the Oracle XE 11.2 setup for Linux from https://www.oracle.com/technetwork/database/database-technologies/express-edition/downloads/xe-prior-releases-5172097.html and place it in
vagrant/develop/assets/oracle/docker/setup/packages/
(You need an Oracle login to do that).Build the Vagrant box (=VM) once, which takes a long while, and start it.
# assuming you are in the projects' root directory: cd vagrant/develop vagrant up # have several cups of tea...
After code changes
Start the Vagrant box and log in
# assuming you are in the projects' root directory: cd vagrant/develop vagrant up && vagrant ssh
Refresh the code
To make sure all dependencies inside the VM are in sync with the files on your host (e.g., after code changes), refresh them inside Vagrant:
sudo su oxi-refresh # maybe start with a clean DB: oxi-initdb
Run the tests
docker start mariadb docker start oracle cd /code-repo cd core/server PERL5LIB=./ prove -r t cd ../.. cd qatest PERL5LIB=./ prove -r backend/api2 backend/webui client cd ..
Using a local dev environment¶
Prerequisites: Database, Linux packages etc.
Once
Set up a running OpenXPKI instance as described in Quickstart guide.
Please note that the tests currently use the database that is configured in /etc/openxpki/config.d
.
After code changes
Update required Perl according to current Makefile
# assuming you are in the projects' root directory: cpanm Carton ./tools/scripts/makefile2cpanfile.pl > cpanfile carton install
Run the tests
# assuming you are in the projects' root directory: cd core/server prove -I ../../local/lib/perl5 -r t cd ../.. cd qatest prove -I ../local/lib/perl5 -I ../core/server -r backend/api2 backend/webui client cd ..
Automatically via Travis-CI¶
Whenever a new commit of the code is pushed onto GitHub, a Travis-CI test run is triggered that runs all of the active tests.
You can find the results at https://travis-ci.org/openxpki/openxpki.
For more details see .travis.yml
in the projects’ root directory.
Writing tests¶
Tests are important and we are glad if you want to contribute a test, e.g., for a bug you have found or a new/untested feature!
OpenXPKI itself is quite complex. That is why there is a bunch of Perl classes that help minimizing the boilerplate code you have to write in each test. They also do some of the tricky setup in the background so you should be able to concentrate on the test logic.
Please have a look at the documentation of OpenXPKI::Test
to start and
understand how the test class(es) work.
Please note that there are still old tests around which do not use the new test class. They will be migrated over time.
Glossary¶
- CA Rollover
- Using multiple CA Certificates with overlapping validity to issue certificates for the same purpose and namespace. CA Rollover allows for continuous and uninterrupted operation of a PKI.
- PKI Realm
- A PKI realm is a “Logical Certificate Authority” which usually includes one or more [Issuing_CA] that are responsible for issuing certificates within the same name space.