Session Sharing and EJBCA
Session Sharing and EJBCA
As of version 9.4, EJBCA can be run in "session sharing" mode. With a
correctly configured Application Server, EJBCA will share user session data
across all nodes in a cluster. This allows EJBCA installations to be fronted by a
load balancer and for instances of EJBCA to be added and removed, restarted and
upgraded, and to potentially fail, all without interruption of service. It
also allows the seamless sharing of load across all instances of EJBCA in the
cluster.
One important limitation of running EJBCA in Session Sharing mode is that
cryptotoken passwords are not shared across nodes unless the token is in
"auto-active" mode. This means that only auto-activated cryptotokens will work
in session sharing mode.
Session sharing and peering
Special care must be taken when configuring peering of load balanced EJBCA
instances. Because EJBCA peering is implemented by creating a long-hanging
HTTPS connection between the source peer and the destination peer. If the
destination peer (typically, an RA) is implemented as multiple EJBCA instances
behind a load balancer, only one instance will see the incoming connection.
The other nodes in the cluster will be unaware of the existence of the peer and
will not operate correctly.
This means that if the destination peer is behind a load balancer, there must
be a second route to each member of the cluster as well. The outgoing peer
would be configured to peer with the individual nodes, rather than the load
balanced address. Clients would still be configured to hit the load balanced
instance.
How to run EJBCA in HA mode
HIGH_AVAILABILITY:
Setting this environment variable to TRUE configures Wildfly in session
sharing mode.
JGROUPS_K8S_NAMESPACE and JGROUPS_K8S_LABEL:
If deployed in kubernetes, set these to the namespace in which EJBCA nodes
are deployed and a label that can be used to discover EJBCA nodes. If
both are not set, peer discovery uses the JDBC_PING protocol, which stores
node names in the EJBCA database.
JGROUPS_KEYSTORE_SECRET and /opt/keyfactor/secrets/external/jgroups-key.p12:
If a symmetric key is provided in
/opt/keyfactor/secrets/external/jgroups-key.p12 with alias jgroups, it
will be used to encrypt inter-node communications. If not, session data
will be shared in the clear. JGROUPS_KEYSTORE_SECRET is the password
for the p12 file (if not supplied, defaults to changeit).
How to run Session Sharing without using the EJBCA Container
The details of configuring a Wildfly or JBoss application server in HA mode is
beyond the scope of this document. The default standalone-ha.xml
configuration can be used to run EJBCA. However, that configuration
distributes not just web sessions, but also EJB instances. EJBCA is not
designed to take advantage of distributed EJBs and running with distributed
EJBs will incur an unnecessary performance cost. Enabling thedistributable-ejb subsystem is not recommended.
Run using standalone-ha.
Disable distributed EJBs
Enable AJP
How to set up an Apache load balancer
High Availability EJBCA configurations assume a load balancer will distribute
traffic across the cluster. This configuration terminates HTTPS at the Apache
load balancer and uses the AJP protocol to communicate with individual EJBCA
instances. AJP sends the client certificate to the application server, which
is convenient for EJBCA when using client certificate authentication.
Below is an Apache configuration that can be used to front two EJBCA instances,
listening on ports 8009 and 8109. Be sure to supply a TLS keypair
(possibly self-generated) and after configuring a Management CA, write it to
the path configured below to support client-cert authentication.
<IfModule mod_ssl.c> <VirtualHost *:443> ServerAdmin webmaster@localhost ServerName localhost ServerAlias localhost LogLevel debug ssl:warn ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined SSLEngine on SSLCertificateFile /etc/ssl/ha/localhost.pem SSLCertificateKeyFile /etc/ssl/ha/localhost-key.pem SSLCACertificateFile /etc/ssl/ha/managementca.pem SSLVerifyClient optional SSLVerifyDepth 3 SSLOptions +ExportCertData <Proxy *> AddDefaultCharset Off Order deny,allow Allow from all </Proxy> <Proxy "balancer://wildfly"> BalancerMember "ajp://localhost:8009" BalancerMember "ajp://localhost:8109" </Proxy> ProxyPass "/" "balancer://wildfly/" ProxyPassReverse "/" "balancer://wildfly/" </VirtualHost></IfModule>How to run with Docker
This starts a database server and two EJBCA instances, all communicating on
a private network (called ha). The ejbca instances export ports 8009 and
8109 to the host machine and can be fronted with the Apache AJP proxy described
above:
docker network create hadocker run --network ha --rm -d --name db -h db \ -e MARIADB_ROOT_PASSWORD=foo123 \ -e MARIADB_DATABASE=ejbca \ -e MARIADB_USER=ejbca \ -e MARIADB_PASSWORD=foo123 \ mariadb:latest # generate a key shared by the clusterkeytool -genseckey -alias jgroups -keyalg aes -keysize 256 -keystore jgroups.p12 -storetype PKCS12 -storepass foo123docker run \ --mount type=bind,source=$(readlink -e jgroups.p12),target=/mnt/external/secrets/jgroups-key.p12 \ --rm -d -p 8009:8009 --network=ha \ -e HIGHAVAILABILITY=true \ -e JGROUPS_KEYSTORE_SECRET=foo123 \ -e PROXY_AJP_BIND=0.0.0.0 \ -e DATABASE_USER=ejbca -e DATABASE_PASSWORD=foo123 \ -e DATABASE_JDBC_URL=jdbc:mariadb://db:3306/ejbca?characterEncoding=utf8 \ --name ejbca1 ejbca-ee:hadocker run \ --mount type=bind,source=$(readlink -e jgroups.p12),target=/mnt/external/secrets/jgroups-key.p12 \ --rm -d -p 8109:8009 --network=ha \ -e HIGHAVAILABILITY=true \ -e JGROUPS_KEYSTORE_SECRET=foo123 \ -e PROXY_AJP_BIND=0.0.0.0 \ -e DATABASE_USER=ejbca -e DATABASE_PASSWORD=foo123 \ -e DATABASE_JDBC_URL=jdbc:mariadb://db:3306/ejbca?characterEncoding=utf8 \ --name ejbca2 ejbca-ee:haThis deploys an EJBCA container with no management CA, so do not send a client
certificate when asked by firefox.
How to run with Kubernetes
Below is a sample deployment. It will start a database and two EJBCA
instances. It exports EJBCA's HTTPS interface on port 9443.
Note that this uses a management CA called CommonManagementCA that was
previously created. Since you won't have a key/cert issued by that CA, you
should replace it with a CA's certificate that has issued you a keypair that
you can use to log on.
---apiVersion: v1kind: ServiceAccountmetadata: name: jgroups-service-account namespace: default---kind: ClusterRoleapiVersion: rbac.authorization.k8s.io/v1metadata: name: jgroups-pod-reader namespace: defaultrules:- apiGroups: [""] resources: ["pods"] verbs: ["get", "list"]---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: jgroups-api-access namespace: defaultroleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: jgroups-pod-readersubjects:- kind: ServiceAccount name: jgroups-service-account namespace: default---apiVersion: v1kind: Secretmetadata: name: jgroups-secret-keydata: # password is foo123 jgroups-key.p12: MIIBowIBAzCCAU0GCSqGSIb3DQEHAaCCAT4EggE6MIIBNjCCATIGCSqGSIb3DQEHAaCCASMEggEfMIIBGzCCARcGCyqGSIb3DQEMCgEFoIHDMIHABgsqhkiG9w0BDAoBAqCBsASBrTCBqjBmBgkqhkiG9w0BBQ0wWTA4BgkqhkiG9w0BBQwwKwQUzGN8gld90iZGBuClAmX+hXT4M88CAicQAgEgMAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBD2uU7a86GyeLXOmnkBh2B9BEBIBRpFUbVc+D250nZfY5juOrvrG5HpAUp9KSOPSEXYaUx6ttc3CiPYd6AoYkJw1ORwuD7Zk49gm0A+5mdu97wiMUIwHQYJKoZIhvcNAQkUMRAeDgBqAGcAcgBvAHUAcABzMCEGCSqGSIb3DQEJFTEUBBJUaW1lIDE3MzA0NzA2MTE2NTUwTTAxMA0GCWCGSAFlAwQCAQUABCAY22nZajFAhxnse7AJOMSIEfGGPkTdxuJNOAqBGl5+sAQU5Gyj++e1YtQ1GFZk/eg3UbAwobcCAicQ---apiVersion: v1kind: Secretmetadata: name: server-tls-keystoredata: # password is serverpwd - set in APPSERVER_KEYSTORE_SECRET # serverpwd b64-ed server.storepasswd: c2VydmVycHdkCg== server.jks: | MIIKvAIBAzCCCmYGCSqGSIb3DQEHAaCCClcEggpTMIIKTzCCBbYGCSqGSIb3DQEHAaCCBacEggWj MIIFnzCCBZsGCyqGSIb3DQEMCgECoIIFQDCCBTwwZgYJKoZIhvcNAQUNMFkwOAYJKoZIhvcNAQUM MCsEFC4siaVhhHlUi/I89LdOnBloLAmoAgInEAIBIDAMBggqhkiG9w0CCQUAMB0GCWCGSAFlAwQB KgQQSFJyYSura9u4gFOOCwVFHASCBNDDHwbKTapUbJiDVbnoFJb8o9kqwQYxfSvKlTO/Na5+xVLb GRlFa39mTPCp1DeEPMWngn7V5HVXkDc1EhH6O/3sdZRS3dy7C8Bx7wVPZ4Mt+rIfdr+DAIN9fZpH xZKUPVIaCBCnNc/R8/n07M/wxCd3S5JZWYfWsctnchdSgQdcx/R5ZG/JErL234KUFwviAPDO68Db Z38YPN6uTBypNk9c7wzMYQpYoBll9hkMdTQWdlMTwgSmdtK06F7p8HNX5oNWZ9z4kPMdcCZ5XA6H KNpHuBXH8bGalbgfN9QdixaQpw4DqOd8YzEx6X74R5YWzUAabn3+KyHhb6WAb26PZhXJQXlyihN4 n6n50hgDoRTi0hqgsTB1Q23+vuI0v0jsRvMRhRL2bKI7ZH3CMpzpEgN9FH/5uhnEKRHsnrKne/OY 1nrrq1CRLTV3mk1BTg3WYfHTiyxPg2vtw+ZCgJa4IVi+TWnS8uyLrq/dZCS7eZfP8AiR0VzealyI 2/CGN6K8uALvy+rk2x8CIToh8dkaeIIAP9FaoHFfFXs/H6VhavuntG+WyjkubnFfy1bW8q4B2aMf 4+eW5za3uMh/xXw92kNcYKuCnb1JUmkJgmQDC+ayzflfJa5YYvp3NpRiDS5a+0yrIzSqJipkz7yJ PjlN+RHRQq8DTmrqVUxOKvSh7PbaUefn5kG/L3GaWQqJT6reEGCE1SeWx9xOGATYM8qjrWKkAJ// 7f9/O8RHBsiXt2521+o625l1906Cp0Iuu3xp9HV9o4v4Sl2ln9/gVG/VMMdgLZp6rVjUcvWq3O4V j2I5qCfkmh9CZEKPlaIjHGBO0cPZrOL81FyRe/s9QXDa6JLnYyeCJBc8ZMDGQ4uwBlrFUNPD0oTN 3oBq4OcpLDcBdQRJNEt0NLI270KBKMsFjZl9IY/rT5ubDrNPiay7PfpR17HaPATJDTeVsX0nwKjS McFa9/wFE7fmbZSgZ4FbMK0ZWPIs61AT+uVcEGhnFh4W4qbV7RpYnBrSZODQOh335D2Nx0mmcUD+ Zhx7HKNFjOCpt2h8x0wDUoMcnYssw3wWCCurqxUzSnGqFbAb0ukHK44bt883dt4JE/McB+pQkG78 lybHcoaIlXwo8IWUuc+UjalJeteSpCev8konZ/J/kfASQQAM5eE2R4cV0nJKjdIzL2qewQ85Yb9u f+IZPU8QJaKuitqyefkBKZsY2inZ+5nBnP2JH35yT6x3hmXW0o+K1T1vQWYKbfSiZQzJBbq60CbG 8GBykS2PvxRg8aVXawZ8gfrh8/Z8/O24PvDg/ASRynkhdhHkcFgSiT/h61/KPYCobRKtht1h2pSr pYeg42B7MM0tDUrw3uA+h24USx1GJ12EWmjgnVLpc92AflnMY8b6a9TE1e1eQqpP0udAULPpZDj5 5mP1fL32boetWogbXq5rpmU2aZjAtAtV388HWx9vSo+PF9k4hamsXgo/54C4Tpwd16sKwIbczUBt EGOLInLhecusNQG50vLQYI/MHGsnGIu8i4wPXBJBPUWylMV5dKJvb5Q9b/XnrDmjFe0C8PUpedHq fnkpI9/1LDD6uVXkU4Ca1gTOX3mzHpCigQPN01RcxDgPRDhmv3WVpaufHoqFUzKSlw/O8+XcrYeB eTFIMCMGCSqGSIb3DQEJFDEWHhQAcwBlAGwAZgBzAGkAZwBuAGUAZDAhBgkqhkiG9w0BCRUxFAQS VGltZSAxNzM1ODIzNzI0MjA2MIIEkQYJKoZIhvcNAQcGoIIEgjCCBH4CAQAwggR3BgkqhkiG9w0B BwEwZgYJKoZIhvcNAQUNMFkwOAYJKoZIhvcNAQUMMCsEFDAzfyjAzwmxRKhfWoDj/e1Hbo+7AgIn EAIBIDAMBggqhkiG9w0CCQUAMB0GCWCGSAFlAwQBKgQQoAqk3aC5oUQmoqZgeOW544CCBAC00Zhe U94DGolIfWbTQm/BoqHSduJKisGWbNPSEGXoCEHUMUwy/69LXaLMfo0z14jdMn8yS3jCUcGsa9Qv 4r4PfpWAjCaYsyW3za/a+M/sZpByqC2yuUuCTxyAnlg4JVYZ13hyDorzUsbzyP3zj78K1xgcIRbW btvdcuzv8kPl7m7G3imB8t8vYUH3zQ3rrxKwxedSuQaPWzAWnCCFD9mDcAknoS1E6YW+dnZuQiwm FkN9aVUdajqO+ODUElo889ukAoBR9jIPk0nsvtqg+ciUIWQqoK1ZGh5WODkdDQK2kdjvDY+0wnEo cocUMxXfK9PNNRpJew/qqraOSIH15QJ3Asb1OtEch1Ci0rGm7fZxJdGq3YX6NhXrqWWhqMXS/y2R TFmU6EBzKxAlI7RuGBUs8od1Lp9CzBwpHIOWCFnlGYtj37uABALP6/GrTW4B04mxRyswWzvDcRgB +h8BDv1+Cztq7lOdZMq9gClxpvxPi5eEPTzr5ISktj+Bsb3UVfRzVgIv52bsS4TT1Kn212+nuFF/ 18XNuX5slxyDiHAYeBHXAYrdseOlnQGBZANkEqOd7BxKQtGOAjWed47HpSIAWe+Gj6HbMOhHQ3t7 KOC7MNr9Pm+NN1QXJhaXSWw61cgKXACSwk8sZQbiIVTkGT/6+I6BA6bW6Buo17phTZBNkjf/KU56 H8XUaAXxs2VcnwRe27qxJMnsByhRyd4zc2ZVavLGwtFoRrbuBryI1twvwM6fey1hzLWk4HnXqwFX sYWGcCTux3n3Da9HNS+XTotPEzqqVpJqG03FGK692kK5Th4Ta6oLQiJULUgsoI9ENiiCXint5rKM zEHAGz/XMk6KFOrGv+nshaw6W9Fjy0PFeX6LKpZb/Fe5qszm8WV0awa41RkFhF1ENdIj6yeF5C+7 NVOQmN+zrOArRfWgmo8eVdvIVFOHVhJjG735Da2ntiAdKGG1kJS/mBwtFySIhnbsptXt4WMzbuzW Z8HjyRHhUUblROtB0rLXxMa73Au5KYd0Jh4EtIH1m4+5Tv97W6n65tP8bYNF0XXdAKaegarHdPi0 lWLu+GlaE4wfkfQuIVB5MeVmpN3SBgVxIGo5bS0fu+N+jHavyVC2c0TAGguJmz/4ZeWC6UWnHCxc RJMarYk7MrlaWouJZZRTxtwJVRQzUdYdj2bN0R2Bmbq3HIa4AGzfu9KDDDeJuRVwT6sd46El2hos bCpcjyp2PjbjEyhIuol8UDUo2jq3pkxPXBk1tYx/bctrekXL1cdeZpbWHjq1JpITIg5VZIHgwip9 KrhpAg1soZZHKk85bqNrpNSljDVjy8t0aUnrZ0JkqnfpM8tB9+xFRsw+v9V/miY0Wv3eME0wMTAN BglghkgBZQMEAgEFAAQgc+Mos1WyfzBOpIxwkN++fAL8nFBW0MBLNvByTnjKjU8EFKFE8OO21+eP BFbvLlNYGgnYkXHkAgInEA==---apiVersion: v1kind: ConfigMapmetadata: name: common-management-ca-certbinaryData: CommonManagementCA.crt: | MIIFUzCCAzugAwIBAgIUekO4J+duFkmfNfx7AX3arTeSvY4wDQYJKoZIhvcNAQELBQAwMTEbMBkG A1UEAwwSQ29tbW9uTWFuYWdlbWVudENBMRIwEAYDVQQKDAlLZXlmYWN0b3IwHhcNMjQxMjAyMTcz NDEzWhcNMzQxMTMwMTczNDEyWjAxMRswGQYDVQQDDBJDb21tb25NYW5hZ2VtZW50Q0ExEjAQBgNV BAoMCUtleWZhY3RvcjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMyC/tmOx/eWwsuF ncYRSbulVjfmxIWoNcR5GcsL5fzUY9dvz46d1BqXVsevKyguZMcsHLjq/EtDGygaR2yHh+KEqHTV edxG23cE4tKvFc9htGY1idBy7rcxqzgEO+aUFcqvxfwiXUwFrJhXZ+I/FKXsrSJkt5pfBLoGRR9Q Yf2QwJd1oWSiWYURkOVUYaOUFF8B3v6m9o2vLDs4absaLytSxFzgHO86BI97k5EVG42Tq9U330iN 6L1TrcmJ1ZMm9/Vw3YOLO+QlnMD+uwK5wl5RuSxH6zeMhCXeqp3zgdWW5jjgZs5AG6ZHZ+X8EyH5 4iqXIJd3YMCxcyK/cPHAtYlBPe3Z8DZDKcmhPfYSuH1wKJ5JFAUfQhIq+q6dQXQJZ97InI9usjXs n/RLJ9cDf1Xi2rTsXBqLd0/Jim+7oESZk2ogzeu5vSYRMF2W0LEM0O6mr8ag1DqSRtdMdbjtZAql oweETqzv9Iy9nqm02IKAuf3mQsCWY7vRiE8k4A5P2V6YxK8kNeRMsnWewWukrcScF3+twCPiihCH xxrWiqDoEwNJm2TF6Vj+x/D7zFIuEHtbd+qJviiJ1+/8e6Zha/Zax4/gtGK7vYzoYMLquBuAwTrA Va2cyzy75nY5LKrQSbUAWicCCmL2P9WuqRhyAQIr9/X4ueiwwEvVmK75bHcrAgMBAAGjYzBhMA8G A1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUWCgGXhU2SMwDM71hdhVSKJaeNV0wHQYDVR0OBBYE FFgoBl4VNkjMAzO9YXYVUiiWnjVdMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEA mcpgd9DOX1vZQndFdnSppptgzWYhwKPusDjW4NUF6wImmqu9630TLcWoGVTuQkjUzMRW45lnyGPB BOhv6iYXoJstRjV+rVK8Fqdqc1Xeq3VQH+UzXvTAeUGwljAoJZPVhMmi/bSncw5NHYwdYSKh0sSp a1esVbz8DJ348BH+9070H1rWvbjbE4p+UbxLlt52g9BGkQQUx/fvnV4/6ANF6huJjIZWv5U/YUb7 Jp5xLIrfM6aX9Z2OdnikW32lwUTojPEmDhawOuOr1cQGIm4JI/8EfFHxdfXpYLiJJoEV9dZpUzGb 3Pegt8bMO33ieO2eK/KY1Vk4xgzyHhiS5ZOvbyK/UnF9pQYVaTHWAfZZWQkKBfoJbR3wAvsd8RKV oLEBYjwjnhRhbgVVdLNv7fPyaBfatxloFpLihLaUJX5EMUp9yUmbFBn1kiDxswRfP4uT61VF7nMx ctshoYCwE6/oYNDzc3VuC24lcbCT6ijS0sL478MWVHPnShHvKM15ye8gvdzDK0mYdKOb6JKVmntl TqHY+ox2ivYy+dT4Zpzx/Z4FAgsFSrY3mSJ51DA/zHg4mNT6mF9Qsb3hM6gfL+WSVhxRIVtQI7O2 461uTUNbrao31BRtyzkFk4H8pzro+gILUoanpw0uFYTRB7FFtgEAZCGP623ftd7uC6j/PUwk4zs=---apiVersion: v1kind: Servicemetadata: name: db labels: app: dbspec: ports: - port: 3306 protocol: TCP selector: app: db---apiVersion: v1kind: Servicemetadata: name: ejbca-servicespec: selector: app: ejbca ports: - port: 9443 targetPort: 8443 type: LoadBalancer---apiVersion: apps/v1kind: Deploymentmetadata: name: db labels: app: dbspec: replicas: 1 selector: matchLabels: app: db template: metadata: labels: app: db spec: containers: - name: db image: mariadb:latest ports: - containerPort: 3306 env: - name: MARIADB_ROOT_PASSWORD value: foo123 - name: MARIADB_DATABASE value: ejbca - name: MARIADB_USER value: ejbca - name: MARIADB_PASSWORD value: foo123---apiVersion: apps/v1kind: Deploymentmetadata: name: ejbca labels: app: ejbcaspec: replicas: 2 selector: matchLabels: app: ejbca template: metadata: labels: app: ejbca spec: containers: - name: ejbca image: ejbca-ee:ha imagePullPolicy: Never volumeMounts: - mountPath: /mnt/external/secrets/tls/cas name: common-management-ca-cert readOnly: true - mountPath: /mnt/external/secrets/ name: jgroups-secret-key readOnly: true - mountPath: /mnt/external/secrets/tls/ks name: server-tls-keystore readOnly: true ports: - containerPort: 8443 name: httpd-http - containerPort: 8080 name: http - containerPort: 7600 name: jgroups-tcp - containerPort: 57600 name: jgroups-tcp-fd env: - name: HIGHAVAILABILITY value: "true" - name: JGROUPS_KEYSTORE_SECRET value: foo123 - name: JGROUPS_K8S_NAMESPACE value: default - name: JGROUPS_K8S_LABEL value: app=ejbca - name: DATABASE_USER value: ejbca - name: DATABASE_PASSWORD value: foo123 - name: DATABASE_JDBC_URL value: jdbc:mariadb://db:3306/ejbca?characterEncoding=utf8 - name: APPSERVER_KEYSTORE_SECRET value: serverpwd - name: INITIAL_ADMIN value: CommonManagementCA;CertificateAuthenticationToken:WITH_COMMONNAME;SuperAdmin - name: TLS_SETUP_ENABLED value: "false"# readinessProbe:# httpGet:# path: /ejbca/publicweb/healthcheck/ejbcahealth?ca=none# port: 8080# initialDelaySeconds: 30# periodSeconds: 5 serviceAccountName: jgroups-service-account volumes: - name: jgroups-secret-key secret: secretName: jgroups-secret-key - name: server-tls-keystore secret: secretName: server-tls-keystore - name: common-management-ca-cert configMap: name: common-management-ca-cert items: - key: CommonManagementCA.crt path: CommonManagementCA.crt