Technical Articles
Enable TLS for SAP Content Server 6.50 with Docker
1 Introduction
For security reasons, in production environments all hypertext transfer communication should be encrypted, meaning you should use https instead of http.
This blog post will show how to enble TLS for SAP Content Server with Docker Image.
Therefore we need a X.509 certificate chain. The cheapest method is creating your own self-signed root certificate authority (root CA) and intermediate certificate authority (CA) to create server certificates.
More details about certificate chain can be found on https://en.wikipedia.org/wiki/Chain_of_trust
2 Main Part
2.1 Prerequisites
If openssl is not already available you can install it on ubuntu with:
sudo apt-get install openssl
2.2 Main certificate directory
Create a main folder to manage your certificates.
2.3 Root CA certificate
Create a sub-folder for the root CA.
mkdir root-ca && cd $_
2.3.1 root-ca.conf
Create a root-ca.conf file and start VIM.
touch root-ca.conf && vi $_
Copy and paste the content of root-ca.conf.
# Simple Root CA
# The [default] section contains global constants that can be referred to from
# the entire configuration file.
[ default ]
ca = root-ca # CA name
dir = . # Top dir
# The next part of the configuration file is used by the openssl req command.
# It defines the CA's key pair, its DN, and the desired extensions for the CA
# certificate.
[ req ]
default_bits = 2048 # RSA key size
encrypt_key = yes # Protect private key
default_md = sha256 # MD to use
utf8 = yes # Input is UTF-8
string_mask = utf8only # Emit UTF-8 strings
prompt = no # Don't prompt for DN
distinguished_name = ca_dn # DN section
req_extensions = ca_reqext # Desired extensions
[ ca_dn ]
0.domainComponent = "mydomain"
countryName = "DE"
localityName = "my city"
organizationName = "my organization"
commonName = "my Root CA"
[ ca_reqext ]
keyUsage = critical,keyCertSign,cRLSign
basicConstraints = critical,CA:true
subjectKeyIdentifier = hash
# The remainder of the configuration file is used by the openssl ca command.
# The CA section defines the locations of CA assets, as well as the policies
# applying to the CA.
[ ca ]
default_ca = root_ca # The default CA section
[ root_ca ]
certificate = $dir/$ca/root-ca.crt # The CA cert
private_key = $dir/$ca/private/$ca.key # CA private key
new_certs_dir = $dir/$ca/certs # Certificate archive
serial = $dir/$ca/db/serial # Serial number file
crlnumber = $dir/$ca/db/crlnumber # CRL number file
database = $dir/$ca/db/index # Index file
unique_subject = no # Require unique subject
default_days = 3652 # How long to certify for
default_md = sha256 # MD to use
policy = match_pol # Default naming policy
email_in_dn = no # Add email to cert DN
preserve = no # Keep passed DN ordering
name_opt = ca_default # Subject DN display options
cert_opt = ca_default # Certificate display options
copy_extensions = none # Copy extensions from CSR
x509_extensions = signing_ca_ext # Default cert extensions
default_crl_days = 365 # How long before next CRL
crl_extensions = crl_ext # CRL extensions
# Naming policies control which parts of a DN end up in the certificate and
# under what circumstances certification should be denied.
[ match_pol ]
#domainComponent = optional # Included if present
organizationName = match # Must match 'Simple Inc'
organizationalUnitName = optional # Included if present
commonName = supplied # Must be present
countryName = supplied
[ any_pol ]
domainComponent = optional
countryName = supplied
stateOrProvinceName = optional
localityName = supplied
organizationName = supplied
organizationalUnitName = optional
commonName = optional
emailAddress = optional
# Certificate extensions define what types of certificates the CA is able to
# create.
[ root_ca_ext ]
keyUsage = critical,keyCertSign,cRLSign
basicConstraints = critical,CA:true
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always
[ signing_ca_ext ]
keyUsage = critical,keyCertSign,cRLSign
basicConstraints = critical,CA:true,pathlen:0
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always
# CRL extensions exist solely to point to the CA certificate that has issued
# the CRL.
[ crl_ext ]
authorityKeyIdentifier = keyid:always
Maybe adapt the ca-dn part to more proper values.
When using your own values, make sure that the same values are always used in the following configurations, otherwise the certificate chain will be broken.
( Exit VIM with ESC and :wq )
2.3.2 Sub-directories & additional files for root CA
Create further needed sub-directories.
mkdir private && mkdir certs && mkdir db
Create a empty index file within db folder.
touch db/index
Create a serial number file containing a random number.
echo 08954571 > db/serial
Go back to certificate main folder.
cd ..
2.3.3 Create root CA certificate request
Create root-ca.csr and root-ca.key file with openssl
openssl req -new -config root-ca/root-ca.conf -out root-ca/root-ca.csr -keyout root-ca/private/root-ca.key
Enter a PEM pass phrase (Make something up, but make sure you don’t forget)
2.3.4 Sign root CA certificate
Sign the root CA certificate
openssl ca -selfsign -config root-ca/root-ca.conf -in root-ca/root-ca.csr -out root-ca/root-ca.crt -extensions root_ca_ext
2.4 Intermediate CA certificate
Create a subfolder for signing CA.
mkdir signing-ca && cd $_
2.4.1 signing-ca.conf
Create a signing-ca.conf file and start VIM.
touch signing-ca.conf && vi $_
Content of signing-ca.conf
# Simple Signing CA
# The [default] section contains global constants that can be referred to from
# the entire configuration file.
[ default ]
ca = signing-ca # CA name
dir = . # Top dir
# The next part of the configuration file is used by the openssl req command.
# It defines the CA's key pair, its DN, and the desired extensions for the CA
# certificate.
[ req ]
default_bits = 2048 # RSA key size
encrypt_key = yes # Protect private key
default_md = sha256 # MD to use
utf8 = yes # Input is UTF-8
string_mask = utf8only # Emit UTF-8 strings
prompt = no # Don't prompt for DN
distinguished_name = ca_dn # DN section
req_extensions = ca_reqext # Desired extensions
[ ca_dn ]
0.domainComponent = "mydomain"
countryName = "DE"
localityName = "my city"
organizationName = "my organization"
commonName = "my Signing CA"
[ ca_reqext ]
keyUsage = critical,keyCertSign,cRLSign
basicConstraints = critical,CA:true,pathlen:0
subjectKeyIdentifier = hash
# The remainder of the configuration file is used by the openssl ca command.
# The CA section defines the locations of CA assets, as well as the policies
# applying to the CA.
[ ca ]
default_ca = signing_ca # The default CA section
[ signing_ca ]
certificate = $dir/$ca/$ca.crt # The CA cert
private_key = $dir/$ca/private/$ca.key # CA private key
new_certs_dir = $dir/$ca/certs # Certificate archive
serial = $dir/$ca/db/serial # Serial number file
crlnumber = $dir/$ca/db/crlnumber # CRL number file
database = $dir/$ca/db/index # Index file
unique_subject = no # Require unique subject
default_days = 3652 # How long to certify for
default_md = sha256 # MD to use
policy = match_pol # Default naming policy
email_in_dn = no # Add email to cert DN
preserve = no # Keep passed DN ordering
name_opt = ca_default # Subject DN display options
cert_opt = ca_default # Certificate display options
copy_extensions = copy # Copy extensions from CSR
x509_extensions = email_ext # Default cert extensions
default_crl_days = 365 # How long before next CRL
crl_extensions = crl_ext # CRL extensions
# Naming policies control which parts of a DN end up in the certificate and
# under what circumstances certification should be denied.
[ match_pol ]
#domainComponent = optional # Included if present
organizationName = supplied # Must match
organizationalUnitName = optional # Included if present
commonName = supplied # Must be present
countryName = match
[ any_pol ]
domainComponent = optional
countryName = supplied
stateOrProvinceName = optional
localityName = supplied
organizationName = supplied
organizationalUnitName = optional
commonName = optional
emailAddress = optional
# Certificate extensions define what types of certificates the CA is able to
# create.
[ email_ext ]
keyUsage = critical,digitalSignature,keyEncipherment
basicConstraints = CA:false
extendedKeyUsage = emailProtection,clientAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always
[ server_ext ]
keyUsage = critical,digitalSignature,keyEncipherment
basicConstraints = CA:false
extendedKeyUsage = serverAuth,clientAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always
# CRL extensions exist solely to point to the CA certificate that has issued
# the CRL.
[ crl_ext ]
authorityKeyIdentifier = keyid:always
Maybe adapt the ca-dn part to more proper values.
2.4.2 Sub-directories & additional files for intermediate CA
mkdir private && mkdir certs && mkdir db
Create a empty index file within db folder.
touch db/index
Create a serial number file containing a random number.
echo 08954572 > db/serial
Go back to certificate main folder.
cd ..
2.4.3 Create intermediate CA certificate request
openssl req -new -config signing-ca/signing-ca.conf -out signing-ca/signing-ca.csr -keyout signing-ca/private/signing-ca.key
Enter a PEM pass phrase
2.4.4 Sign intermediate CA certificate
openssl ca -config root-ca/root-ca.conf -in signing-ca/signing-ca.csr -out signing-ca/signing-ca.crt -extensions signing_ca_ext
2.5 SAP Content Server certificate
Create a subfolder for the server certificate
mkdir server && cd $_
2.5.1 server.conf
Create a server.conf file and start VIM.
touch server.conf && vi $_
Content of server.conf
[ req ]
default_bits = 2048
distinguished_name = req_dn
req_extensions = req_ext
default_md = sha256
dirstring_type = nombstr
prompt = no
[ req_dn ]
C = DE
ST = my state
L = my city
O = my organization
OU = my team
CN = sapcs.mydomain.locl
[req_ext]
subjectAltName=@subject_alt_name
[ subject_alt_name ]
DNS.1=sapcs.mydomain.locl
DNS.2=mydomain.locl
Go back to certificate main folder
cd ..
2.5.2 Create SAP Content Server certificate request
openssl req -new -config server/server.conf -out server/server.csr -keyout server/server.key
2.5.3 Sign SAP Content Server certificate
openssl ca -config signing-ca/signing-ca.conf -in server/server.csr -out server/server.crt -extensions server_ext
2.5.4 Remove passphrase from private key
To enable Apache to start automatically without user interaction, we remove the passphrase from the private key of SAP Content Server certificate.
openssl rsa -in server/server.key -out server/server-wopass.key
It is important that no unauthorized person gets access to the file. Otherwise, together with the public certificate, he can pretend the identity of the SAP Content Server.
There is an alternative way to store the password in a bash file (e.g. PassPhrase.sh), which has to be included via SSLPassPhraseDialog exec:/etc/httpd/PassPhrase.sh into httpd.conf file. But in this case the PassPharase.sh file has to be kept safe. (Not much was gained)
2.6 Importing root CA certificates in certificate store
Because we use a self-signed certificate, we need to import the root-ca.crt into the certificate store in the right place. Otherwise we won’t get a trusted certificate chain.
With WSL2 you can use the Explorer from Windows OS together with the \\wls$ network share. In case using Ubuntu as a linux-sub-system you will find the main certificate directory at:
\\wsl$\Ubuntu-20.04\home\<user>\certificates
2.6.1 Import root CA certificate in Windows
Go into the root-ca folder and make a double-click on file root-ca.crt.
Press the marked button.
Choose local computer
and press the button “Continue”.
Now we have to select the proper store for root CAs.
Press “OK”, “Continue” and “Complete”.
2.6.2 Import root CA certificate in Ubuntu
cd root-ca
Copy root-ca.crt to /usr/share/ca-certificates/mozilla/.
sudo cp root-ca.crt /usr/share/ca-certificates/mozilla/
Update ca-certificates configuration.
sudo dpkg-reconfigure ca-certificates
Press Enter and follow the upcomming instructions.
2.7 Check certificate chain
openssl verify -CAfile signing-ca/signing-ca.crt -verify_hostname sapcs.mydomain.locl server/server.crt
2.8 Change Docker Image
2.8.1 Docker Image directory for certificates
Create a folder cert within the folder of the SAP Content Server Docker Image.
Copy server.crt into folder cert.
cp server/server.crt ../docker-images/sapcs/cert
Copy server-wopass.key into folder cert.
cp server/server-wopass.key ../docker-images/sapcs/cert
Copy signing-ca.crt into folder cert.
cp signing-ca/signing-ca.crt ../docker-images/sapcs/cert
2.8.2 Change httpd.conf
Open httpd.conf with VIM.
Part to be inserted
<VirtualHost *:80>
DocumentRoot "/usr/local/apache2/htdocs"
ServerName sapcs.mydomain.locl
SSLEngine on
SSLProtocol -all +TLSv1.2
SSLCertificateFile /usr/local/apache2/conf/server.crt
SSLCertificateChainFile /usr/local/apache2/conf/signing-ca.crt
SSLCertificateKeyFile /usr/local/apache2/conf/server-wopass.key
</VirtualHost>
Insertion position
( Exit VIM with ESC and :wq )
2.8.3 Change Docker File
Open Docker file.
vi ../docker-images/sapcs/Dockerfile
Content of Docker file.
FROM httpd:2.2
COPY ./cs.conf /usr/local/apache2/
COPY ./httpd.conf /usr/local/apache2/conf
COPY ./download/*.so /usr/local/apache2/modules/
COPY ./cert/ /usr/local/apache2/conf
RUN chmod a+x /usr/local/apache2/modules/*
RUN mkdir /usr/local/apache2/RepRoot
( Exit VIM with ESC and :wq )
2.9 Rebuild Image and start Docker Container
Go into main folder of SAP Content Server Docker Image.
cd ../docker-images/sapcs/
Build Docker Image.
docker-compose build
Start Docker Container.
docker-compose up -d
2.10 Check https connection
2.10.1 Change hosts file
So that localhost can be accessed via the FQDN sapcs.mydomain.locl, the hosts file must be adapted.
Open hosts file in editor (e.g. notepad++).
C:\Windows\System32\drivers\etc\hosts
Insert the marked line into hosts file.
2.10.2 Get ServerInfo
Open URL in browser.
https://sapcs.mydomain.locl:1090/ContentServer/ContentServer.dll?serverInfo
Now you should see a closed lock icon in your browser.
Click on the lock icon and you should see that the communication is now trusted.
If you use Firefox, you must first import the root CA certificate into the Firefox certificate store for the connection to be recognized as trusted.
3 Conclusion
As you have seen, most of the work involved in setting up TLS on the SAP Content Server is creating the appropriate certificates.
In a larger production environment, the creation of the root CA and intermediate certification authority may be omitted because they already exist, so that only the appropriate server certificate needs to be created.
But since the handling of certificates is new territory for someone, it was important to me to show how to create certificates from scratch, so that she or he could experiment a little bit with it.
The adaptation of the configuration files of the Content Server is no big effort, as is the renewal of the certificates. In this case, you only need to copy the new files into the corresponding directory of the Docker Image, rebuild the image and start it again.