February 27, 2020

X.509 Certificate Profiles

Mandatory Fields

Serial Number 
Issuer DN 
Validity 
Subject DN 
Subject Public Key InfoContains key algorithm, size and info.

Certificate Extensions

Basic ContraintsCriticalIf CA or not.
Authority Key Identifier Hash of CA public key.
Subject Key Identifier Hash of public key.
Authority Information Access Contains URL to OCSP and CA certificate.
CRL Distribution Points URL to CRL.
Certificate Policies Organization OID for their certificate policy..
Key UsageCriticalKey usage attribute derived from Extended Key Usage.
Extended Key Usage Typical values are from RFC 5280.
Subject Alternative Names Their are different SAN: DNS (for web servers), email (S-MIME) and UPN (Windows login).

RFC 5280

https://tools.ietf.org/html/rfc5280#section-4.2.1.3

Key Usage:

  • digitalSignature -
  • nonRepudiation -
  • keyEncipherment - "subject public key (e.g. RSA) is used for enciphering private or secret keys"
  • dataEncipherment - "NOTE that the use of this bit is extremely uncommon"
  • keyAgreement - "subject public key is used for key agreement (Diffie-Hellman key)"
  • keyCertSign - "If set then CA bit in the basic constraints extension MUST also be set"
  • cRLSign -

Extended Key Usage:

  • serverAuth - Key Usage may be: digitalSignature, keyEncipherment or keyAgreement
  • clientAuth - Key Usage may be: digitalSignature and/or keyAgreement
  • codeSigning - Key Usage may be: digitalSignature
  • emailProtection - Key Usage may be: digitalSignature, nonRepudiation, and/or (keyEncipherment or keyAgreement)
  • timeStamping - Key Usage may be: digitalSignature and/or nonRepudiation
  • OCSPSigning - Key Usage may be: digitalSignature and/or nonRepudiation

EJBCA CE ROOT CA


Basic Constraints - CRITICAL

CA:TRUE

Path Length Constraint: Unlimited

Authority Key ID

Subject Key ID

Key Usage - CRITICAL:
  digitalSignature
  keyCertSign
  cRLSign
  
Extended Key Usage:
    -

Dogtag caCert


https://github.com/dogtagpki/pki/blob/master/base/ca/shared/conf/caCert.profile

Basic Constraints - CRITICAL

CA:TRUE

Path Length Constraint: Unlimited

Authority Key ID

Subject Key ID

Key Usage - CRITICAL:
  digitalSignature
  nonRepudiation
  keyCertSign
  cRLSign
  
Extended Key Usage:
    -

EJBCA CE SERVER


Basic Constraints - CRITICAL

Authority Key ID

Subject Key ID

Key Usage - CRITICAL:
  digitalSignature
  keyEncipherment
  
Extended Key Usage:
    serverAuth

Dogtag rsaServerCert


https://github.com/dogtagpki/pki/blob/master/base/ca/shared/conf/rsaServerCert.profile

Authority Key ID

Key Usage - CRITICAL:
  digitalSignature
  dataEncipherment
  keyEncipherment

Extended Key Usage:
    serverAuth

EJBCA CE END USER


Basic Constraints - CRITICAL

Authority Key ID

Subject Key ID

Key Usage - Critical:
  digitalSignature
  nonRepudiation
  keyEncipherment
  
Extended Key Usage:
    clientAuth
    emailProtection

Dogtag rsaSubsystemCert


https://github.com/dogtagpki/pki/blob/master/base/ca/shared/conf/rsaSubsystemCert.profile

Authority Key ID

Key Usage - Critical:
  digitalSignature
  nonRepudiation
  dataEncipherment
  keyEncipherment
  
Extended Key Usage:
    clientAuth

EJBCA CE OCSP


Basic Constraints - CRITICAL

Authority Key ID

Subject Key ID

Key Usage - CRITICAL:
  digitalSignature
  
Extended Key Usage:
    OCSPSigning 

OCSP No Check

Dogtag caOCSPCert


https://github.com/dogtagpki/pki/blob/master/base/ca/shared/conf/caOCSPCert.profile

Basic Constraints - CRITICAL

Authority Key ID

Subject Key ID

Key Usage:
  -
  
Extended Key Usage:
    OCSPSigning 

com.netscape.cms.profile.def.OCSPNoCheckExtDefault

February 7, 2020

Tomcat RPM installation on RHEL and CentOS

Introduction

Using a RPM installation from a trusted RPM repository is a huge advantage, since then is life cycle with patching made easy. Otherwise you will have to manage all patching yourself, which in the long run, will always be more costly than using RPM installation from a trusted RPM repository. And you will also be more exposed by security weakness, because you have not a current patched software version.

RHEL 7 and CentoOS 7

Both RHEL 7 and CentOS 7 comes with a ready to use Tomcat 7. The only drawback is that the version is rather old.

But do you really need a newer version? The difference between Servlet 3.0 (part of Java EE 6) and Servlet 4.0 (part of Java EE 8) is quite small.

And if you are using websockets it is the same version of the spec between Tomcat 7 and 9. Other bigger differences is the support for HTTP/2, but do really need this? Maybe you do or you do not. And if you do not use these new feature you are probably fine with an older version of Tomcat.

From a security perspective, the difference between Tomcat 7 and 9 (as long they are patched), are minimal. Alla the security configuration in web.xml are in place in Tomcat 7. See (Mis)Configure web.xml in Java EE 6 . The only new feature in Servlet 4 and web.xml is

  1. default-context-path
  2. request-character-encoding
  3. response-character-encoding
  4. deny-uncovered-http-methods
  5. absolute-ordering (only relevant if you are using web-fragment.xml)

And the difference between security specific feature between Tomcat 7 and 9 are

  1. Tomcat 9 new supports OCSP, but only for Tomcat Native Connector. See https://tomcat.apache.org/tomcat-9.0-doc/ssl-howto.html#Using_OCSP_Certificates
  2. Both Tomcat 7 and 9 both rely on MBean technology for monitoring, which is a problem, since remote JMX connection does not support strong authentication, only username and password and default requires random ports. The random ports can be fixed with catalina-jmx-remote.jar, see Enable JMX Remote in Tomcat 7
  3. Pure Tomcat High Availability (HA) is the same, i.e. 1. replication is the same, but do you really need this, maybe you do or you do not. 2. Pure Tomcat load balancing is seldom used in production. If you are looking for a software solution I recommend using mod_cluster, which works very well. If that is not an option use mod_proxy. Otherwise most people use a hardware load balancer like BigIP F5.
  4. Tomcat 9 still have crazy logging handling. 1. logging to multiple files which make error search hard and general overview of the tomcat health hard. 2. The only project using custom logging handler JULI. Do Java really need another logging framework? Consider using a single log file and Log4J, see Use Log4J in Tomcat 7

Otherwise the good security filter are still the same, such as CORS Filter, CSRF Filter, Expires Filter, Failed Request Filter and HTTP Header Security Filter, see Tomcat 7 Container Provided Filters.

The nest lockout realm, see Tomcat 7 Lockout Realm.


# yum install tomcat

# systemctl restart tomcat; systemctl enable tomcat

# rpm -ql tomcat
/etc/logrotate.d/tomcat
/etc/sysconfig/tomcat
/etc/tomcat
/etc/tomcat/Catalina
/etc/tomcat/Catalina/localhost
/etc/tomcat/catalina.policy
/etc/tomcat/catalina.properties
/etc/tomcat/conf.d
/etc/tomcat/conf.d/README
/etc/tomcat/context.xml
/etc/tomcat/log4j.properties
/etc/tomcat/logging.properties
/etc/tomcat/server.xml
/etc/tomcat/tomcat-users.xml
/etc/tomcat/tomcat.conf
/etc/tomcat/web.xml
/usr/bin/tomcat-digest
/usr/bin/tomcat-tool-wrapper
/usr/lib/systemd/system/tomcat.service
/usr/lib/systemd/system/tomcat@.service
/usr/libexec/tomcat
/usr/libexec/tomcat/functions
/usr/libexec/tomcat/preamble
/usr/libexec/tomcat/server
/usr/sbin/tomcat
/usr/share/doc/tomcat-7.0.76
/usr/share/doc/tomcat-7.0.76/LICENSE
/usr/share/doc/tomcat-7.0.76/NOTICE
/usr/share/doc/tomcat-7.0.76/RELEASE-NOTES
/usr/share/tomcat
/usr/share/tomcat/bin/bootstrap.jar
/usr/share/tomcat/bin/catalina-tasks.xml
/usr/share/tomcat/conf
/usr/share/tomcat/lib
/usr/share/tomcat/logs
/usr/share/tomcat/temp
/usr/share/tomcat/webapps
/usr/share/tomcat/work
/var/cache/tomcat
/var/cache/tomcat/temp
/var/cache/tomcat/work
/var/lib/tomcat
/var/lib/tomcat/webapps
/var/lib/tomcats
/var/log/tomcat
/var/log/tomcat/catalina.out

Configuration:
/etc/tomcat/tomcat.conf
(/etc/sysconfig/tomcat)

# systemctl cat tomcat.service
# /usr/lib/systemd/system/tomcat.service
# Systemd unit file for default tomcat
# 
# To create clones of this service:
# DO NOTHING, use tomcat@.service instead.

[Unit]
Description=Apache Tomcat Web Application Container
After=syslog.target network.target

[Service]
Type=simple
EnvironmentFile=/etc/tomcat/tomcat.conf
Environment="NAME="
EnvironmentFile=-/etc/sysconfig/tomcat
ExecStart=/usr/libexec/tomcat/server start
SuccessExitStatus=143
User=tomcat

[Install]
WantedBy=multi-user.target


# cat /etc/logrotate.d/tomcat 
/var/log/tomcat/catalina.out {
    copytruncate
    weekly
    rotate 52
    compress
    missingok
    create 0644 tomcat tomcat
}

-----------------------------

# mkdir /usr/share/tomcat/webapps/ROOT
# echo "<html><body><h1>TOMCAT ROOT</h1></body></html>" > /usr/share/tomcat/webapps/ROOT/index.html
# chown root:tomcat -Rv /usr/share/tomcat/webapps/
# curl http://127.0.0.1:8080/
<html><body><h1>TOMCAT ROOT</h1></body></html>

-----------------------------
# yum list installed | grep tomcat
tomcat.noarch                     7.0.76-10.el7_7            @rhel-7-server-rpms
tomcat-el-2.2-api.noarch          7.0.76-10.el7_7            @rhel-7-server-rpms
tomcat-jsp-2.2-api.noarch         7.0.76-10.el7_7            @rhel-7-server-rpms
tomcat-lib.noarch                 7.0.76-10.el7_7            @rhel-7-server-rpms
tomcat-servlet-3.0-api.noarch     7.0.76-10.el7_7            @rhel-7-server-rpms

RHEL 8 and CentOS 8

Now too the sad story. Tomcat is no longer available on either RHEL 8 or CentOS 8.

"The Apache Tomcat server has been removed from Red Hat Enterprise Linux. Apache Tomcat is a servlet container for the Java Servlet and JavaServer Pages (JSP) technologies. Red Hat recommends that users requiring a servlet container use the JBoss Web Server."
  1. https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html-single/considerations_in_adopting_rhel_8/index#tomcat-removal_dynamic-programming-languages-web-servers-database-servers
  2. https://forums.centos.org/viewtopic.php?t=71787
  3. https://bugzilla.redhat.com/show_bug.cgi?id=1700823

December 3, 2019

Getting Started with Minikube on Fedora 30

Introduction

To install Minikube there are 3 things to do:

  1. Install a hypervisor, e.g. VirtualBox, KVM
  2. "Install" (download and add in path) kubectl
  3. "Install" (download and add in path) Minikube

Install kubectl

Reference: https://kubernetes.io/docs/tasks/tools/install-kubectl/#install-kubectl-on-linux


$ curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl

$ chmod +x ./kubectl

$ sudo mv ./kubectl /usr/local/bin/kubectl

$ kubectl version

Install MiniKube


$ wget https://github.com/kubernetes/minikube/releases/download/v1.6.0-beta.1/minikube-1.6.0.beta.1.rpm .
    
$ sudo rpm -pi minikube-1.6.0.beta.1.rpm

$ sudo rpm -ql minikube
/usr/bin/minikube

$ sudo rpm -q --scripts minikube

Test

https://kubernetes.io/docs/setup/learning-environment/minikube/

Start Minikube and create a cluster. Here I specify VirtualBox as hypervisor, since I have also KVM installed.


$ minikube start --vm-driver=virtualbox

Deploy image 'k8s.gcr.io/echoserver' version 1.10 and name deployment 'hello-minikube'.


$ kubectl create deployment hello-minikube --image=k8s.gcr.io/echoserver:1.10

Expose deployment on NodePort and port 8080, i.e. create routing.


$ kubectl expose deployment hello-minikube --type=NodePort --port=8080

Check deployment has finished.


$ kubectl get pod

Retrieve internal IP and port and access web app.


$ minikube service hello-minikube --url
http://192.168.99.100:30024

$ curl http://192.168.99.100:30024
Hostname: hello-minikube-797f975945-fmswk

Pod Information:
 -no pod information available-

Server values:
 server_version=nginx: 1.13.3 - lua: 10008

Request Information:
 client_address=172.17.0.1
 method=GET
 real path=/
 query=
 request_version=1.1
 request_scheme=http
 request_uri=http://192.168.99.100:8080/

Request Headers:
 accept=*/*
 host=192.168.99.100:30024
 user-agent=curl/7.65.3

Request Body:
 -no body in request-

December 2, 2019

How to Install VirtualBox 6 on Fedora 30

https://www.if-not-true-then-false.com/2010/install-virtualbox-with-yum-on-fedora-centos-red-hat-rhel/

Introduction to YAML

Introduction

The YAML format has grown in popularity and is used for example in Kubernetes and Ansible.

Comparison XML, JSON and YAML

XML JSON YAML

<Servers>
    <Server>
        <id>12345</id>
        <name>My Web Server</name>
        <status>ACTIVE</status>
    </Server>
</Servers>

{
    "Servers": [
        {
            "id": "12345",
            "name": "My Web Server",
            "status": "ACTIVE"
        }
    ]
}

Servers:
    -   id: 1234
        name: My Web Server
        status: ACTIVE

Tools for validating JSON

There are numerous tools out that for JSON, one for Linux and Bash is jq - a lightweight and flexible command-line JSON processor.


$ sudo yum install jq

$ cat servers.json | jq '.Servers[0].id'
"12345"

Tools for validating YAML

And a Linux and Bash tool for YAML is yq


$ sudo yum install python3 python3-pip
$ sudo pip3 install yq

$ cat servers.yaml | yq '.'
{
  "Servers": [
    {
      "id": 1234,
      "name": "My Web Server",
      "status": "ACTIVE"
    }
  ]
}

YAML Data Types

WARNING: YAML is not structured in the same sense as XML, JSON, XHTML, etc. It uses spaces (and not tabs and do not mix space and tabs) as seperator. So pay extra attention to how many spaces you use.

Key Value Pair


Fruit: Orange
Vegetable: Lettuce
Liquid: Wine

$ cat key_value_pair.yaml | yq '.'
{
  "Fruit": "Orange",
  "Vegetable": "Lettuce",
  "Liquid": "Wine"
}

List (Array)


Fruits:
-   Orange
-   Apple
-   Banana

Vegetables:
-   Carrot
-   Tomatoes
-   Onion

$ cat array_list.yaml | yq '.'
{
  "Fruits": [
    "Orange",
    "Apple",
    "Banana"
  ],
  "Vegetables": [
    "Carrot",
    "Tomatoes",
    "Onion"
  ]
}

Dictionary (Map)


Banana:
    Calories: 105
    Fat: 0.4 g
    Carbs: 31 g

Grapes:
    Calories: 27
    Fat: 0.7 g
    Carbs: 56 g    

$ cat dictionary_map.yaml | yq '.'
{
  "Banana": {
    "Calories": 105,
    "Fat": "0.4 g",
    "Carbs": "31 g"
  },
  "Grapes": {
    "Calories": 27,
    "Fat": "0.7 g",
    "Carbs": "56 g"
  }
}

More Advanced Examples. List of Dictionary (Array of Map)


Fruits:
-   Orange:
        Calories: 105
        Fat: 0.4 g
        Carbs: 31 g
-   Apple:
        Calories: 27
        Fat: 0.7 g
        Carbs: 56 g
        
$ cat array_list_of_dictionary_map.yaml | yq '.'

$ cat array_list_of_dictionary_map.yaml | yq '.Fruits[1]'
{
  "Apple": {
    "Calories": 27,
    "Fat": "0.7 g",
    "Carbs": "56 g"
  }
}

Differences between List and Dictionary

Dictionary - Unordered

List - Ordered

November 29, 2019

How to Install IDM Master and Replica on RHEL 7

Minimum Hardware

4 GB RAM

https://bugzilla.redhat.com/show_bug.cgi?id=1436295

Prerequisite

I have created two virtual machine, since this is a development setup I will hardcode hostname and IP in /etc/hosts and manually set hostnames.

  • rhel7.7-idm-master.magnuskkarlsson.local
    • 192.168.122.113
  • rhel7.7-idm-replica1.magnuskkarlsson.local
    • 192.168.122.99

# cat /etc/redhat-release 
Red Hat Enterprise Linux Server release 7.7 (Maipo)

# systemctl stop firewalld; systemctl disable firewalld

# hostnamectl set-hostname rhel7.7-idm-master.magnuskkarlsson.local
# hostnamectl set-hostname rhel7.7-idm-replica1.magnuskkarlsson.local

# echo "192.168.122.113 rhel7.7-idm-master.magnuskkarlsson.local" >> /etc/hosts
# echo "192.168.122.99 rhel7.7-idm-replica1.magnuskkarlsson.local" >> /etc/hosts

Update Date & Time

Make sure NTP is setup and synchronized.


systemctl restart chronyd
chronyc sources
chronyc tracking
timedatectl

Installation of IDM Master (version 4.6.5)

Install IDM Master on rhel7.7-idm-master.magnuskkarlsson.local.


# yum install -y ipa-server

# ipa-server-install --domain magnuskkarlsson.local \
    --realm MAGNUSKKARLSSON.LOCAL \
    -p foo123123 -a foo123123 -U 

Installation of IDM Replica (version 4.6.5)

Install IDM Replica on rhel7.7-idm-replica1.magnuskkarlsson.local.

First install idm/ipa client and register host in idm. Then setup host as replica.


# yum install -y ipa-server

# ipa-client-install --server=rhel7.7-idm-master.magnuskkarlsson.local \
    --domain=magnuskkarlsson.local \
    --principal=admin \
    --password=foo123123 -U

# ipa-replica-install --setup-ca --principal=admin --admin-password=foo123123 -U

Test

First kerberos login on master - rhel7.7-idm-master.magnuskkarlsson.local and add a user.


# kinit admin

# ipa user-add --first="Magnus K" \
    --last=Karlsson \
    --cn="Magnus K Karlsson" \
    --principal=magnuskkarlsson \
    --password \
    --all magnuskkarlsson

# ipa user-find magnuskkarlsson

Then kerberos login on replica - rhel7.7-idm-replica1.magnuskkarlsson.local and search for user and check that user is replicated.


# kinit admin

# ipa user-find magnuskkarlsson

November 8, 2019

Eclipse Microprofile LDAP Health Check with Java EE 8 and JBoss EAP 7.2

Introduction

In my previous blog I wrote about Eclipse Microprofile Health with Java EE 8 and JBoss EAP 7.2 and pointed to several Health Checks built in Spring, but for LDAP Springs Health Check is not very good. A better implementation is suggested below. And also based on standard Eclipse Microprofile Health.

Eclipse Microprofile LDAP Health Check


package se.magnuskkarlsson.example.microprofile;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;

import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.health.Health;
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;

@Health
@ApplicationScoped
public class LDAPHealthCheck implements HealthCheck {

    private final Logger log = Logger.getLogger(LDAPHealthCheck.class.getName());

    @Inject
    @ConfigProperty(name = "ldapHealthCheck.providerURL")
    protected String providerURL;

    @Inject
    @ConfigProperty(name = "ldapHealthCheck.securityPrincipal")
    protected String securityPrincipal;

    @Inject
    @ConfigProperty(name = "ldapHealthCheck.securityCredentials")
    protected String securityCredentials;

    @Inject
    @ConfigProperty(name = "ldapHealthCheck.baseCtxDN")
    protected String baseCtxDN;

    @PostConstruct
    public void init() {
    }

    @Override
    public HealthCheckResponse call() {
        List<String> result = null;
        try {
            result = searchBaseContextDN();
        } catch (Exception e) {
            log.log(Level.SEVERE, "Failed to perform LDAP health check search baseCtxDN='" + baseCtxDN + "'.", e);
        }
        boolean state = (result != null && !result.isEmpty()) ? true : false;
        String data = (result != null && !result.isEmpty()) ? result.toString() : null;
        return HealthCheckResponse.named("ldap-health-check").withData(baseCtxDN, data).state(state).build();
    }

    protected List<String> searchBaseContextDN() throws NamingException {
        InitialLdapContext ctx = null;
        try {
            Properties env = new Properties();
            env.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
            env.setProperty(Context.PROVIDER_URL, providerURL);
            env.setProperty(Context.SECURITY_AUTHENTICATION, "simple");
            env.setProperty(Context.SECURITY_PRINCIPAL, securityPrincipal);
            env.put(Context.SECURITY_CREDENTIALS, securityCredentials);

            log.info("Logging into LDAP server, env=" + env);
            ctx = new InitialLdapContext(env, null);
            log.info("Logged into LDAP server, " + ctx);

            // filter
            String filter = "(objectClass=*)";

            // scope
            SearchControls ctls = new SearchControls();
            ctls.setSearchScope(SearchControls.ONELEVEL_SCOPE);

            // search for objects using filter and scope
            NamingEnumeration<SearchResult> answer = ctx.search(baseCtxDN, filter, ctls);
            List<String> result = new ArrayList<String>();
            while (answer.hasMore()) {
                SearchResult searchResult = answer.next();
                if (searchResult != null) {
                    result.add(searchResult.toString());
                }
            }

            log.info("Result base context dn, " + result);
            return result;
        } finally {
            if (ctx != null) {
                try {
                    // Close the context when we're done
                    ctx.close();
                } catch (NamingException IGNORE) {
                }
            }
        }
    }

}

Test

http://127.0.0.1:9990/health


[standalone@localhost:9990 /] /subsystem=microprofile-health-smallrye:check