Security Features
The ESF security enhancements improve upon the security features of ESF 3.1.x, increasing the levels of security and flexibility, and making easier for the developer to specify what the code running in an ESF instance can do.
To achieve this result, the security policy applied by ESF is no longer hard coded in the Security Manager; instead, it is externalised in the security.policy XML file.
This file contains the execution modality in which ESF has to run and the different applied policies.
The security policy of ESF supports two execution modalities:
-
development mode - enables some specific features that simplify the development process and provide remote debugging. This is the factory default.
-
production mode - strengthens the security checks; remote debugging and profiling are not allowed.
Warning!
Eurotech recommends the usage of ESF development mode only in the early stages of software development, while suggests switching to production mode for deployed solutions.
The execution modality may be determined through the ESF Gateway Administration Console. In development mode, the lower part of the console displays a warning message as shown in the screen capture below.
In production mode, no warning messages are displayed.
Security Policy File
Definition
By default, the security policy file is located in this path: /opt/eurotech/esf/kura/security.policy.
The following XML file shows the default security policy of a newly installed copy of ESF:
<?xml version="1.0" encoding="UTF-8"?>
<esf:security xmlns:esf="http://eurotech.com/esf/2.0" version="1">
<esf:properties>
<esf:property>
<esf:name>permit-debug</esf:name>
<esf:value>true</esf:value>
</esf:property>
</esf:properties>
<esf:policies>
<esf:policy>
<esf:access>ALLOW</esf:access>
<esf:permissions>
<esf:permission>
<esf:name>java.security.AllPermission</esf:name>
<esf:values>
<esf:value>
*
</esf:value>
<esf:value>
*
</esf:value>
</esf:values>
</esf:permission>
</esf:permissions>
<esf:name>
All permissions to everyone
</esf:name>
</esf:policy>
</esf:policies>
</esf:security>
Various fields used to create a security policy are described in the sections that follow.
Properties Element
The properties element represents the node element where one or more property elements can be appended.
The property element is composed of a name and value element. The supported property element is shown below.
<esf:property>
<esf:name>permit-debug</esf:name>
<esf:value>true</esf:value>
</esf:property>
This property node contains a name string with a permit-debug value and a value element that can be one of the following:
-
true - specifies that ESF should be started in development mode
-
false - specifies that ESF should be started in production mode with all the security checks enabled
Policies Element
To be compliant with the ESF security policy definition, the policies element must contain one or more policy elements, as shown below.
<esf:policy>
<esf:access>ALLOW</esf:access>
<esf:permissions>
<esf:permission>
<esf:name>java.security.AllPermission</esf:name>
<esf:values>
<esf:value>
*
</esf:value>
<esf:value>
*
</esf:value>
</esf:values>
</esf:permission>
</esf:permissions>
<esf:name>
All permissions to everyone
</esf:name>
</esf:policy>
Each policy element has to contain the following child elements:
- access - required node
- conditions - not mandatory node
- permissions - required node
- name - required node
Access Element
The access child element defines the access rule as ALLOW or DENY.
- ALLOW - corresponds to the normal case of saying what is allowed
- DENY - to say what isn’t allowed
Conditions Element
The conditions is a node that specifies the bounds of the rule: the security policy will be applied only to the bundles that match the contained condition. Each conditions node contains one or more condition elements that specify the effective condition.
The condition element contains two children elements:
- name - specifies the name of the standard condition to be used. The OSGi standard specifies two conditions: BundleSignerCondition and BundleLocationCondition.
- value - specifies the effective condition that has to be applied.
The BundleSignerCondition is satisfied when the related bundle is signed with a certificate that matches what in the value element.
The BundleLocationCondition matches the argument in the value element against the location of the bundle. Bundle location matching provides many of the advantages of signing without the overhead. However, using locations as the authenticator requires secure locations that cannot be spoofed.
An example of condition:
<esf:conditions>
<esf:condition>
<esf:name>
BundleSignerCondition
</esf:name>
<esf:value>
"CN=Common Name, OU=Organization Unit, O=Organization, L=Ottawa, ST=Ontario, C=Country; CN=CA common name, OU=CA Organization Unit, O=CA Organization, C=US;-"
</esf:value>
</esf:condition>
</esf:conditions>
The value element is composed by two arguments: the first is a string that represents a matching Distinguished Name (DN). The second argument is optional, if used, it must be an exclamation mark (’!’). The exclamation mark indicates that the result for this condition must be reversed.
The DN value is used to match the one used to sign the bundles and can contain the description of the complete chain of certificates used to sign the bundle. Each certificate of the chain is separated from the others using a semicolon. The usage of wildcards is supported.
Order of certificates in the value element is important: reversing it indicates the opposite signing relationship.
Two wildcards are supported:
- the star (*)
- the hyphen (-)
The start wildcard can be used as part of the right-hand argument of an attribute, such as:
CN=*,O=Organization,C=IT
This matches all the certificates that have every common name (CN) and the correspondent Organization and Country value.
When used alone, the star wildcard matches at most one certificate. For example:
*;CN=Common Name,O=Organization,C=IT
The hyphen wildcard matches zero or more certificates.
Permissions Element
The permissions element contains one or more permission rules that are explicitly defined in the value element contained. The permission element can contain a java permission or an OSGi-related permission. This value must be provided without starting and ending parenthesis in a format that is similar to the default Java policy syntax.
Each permission requires a permission name (e.g., java.security.AllPermission) and one values node to be supplied. The values node may contain one or more value elements depending on the permission definition. Typically, the first value is named Target Name and specifies what the permission will allow or deny.
Another parameter may specify the operation (i.e., read, write, etc.) that will be permitted or denied by the rule.
Java supports several security checks that the developer can specify. Following, a list of them:
- java.security.AllPermission
- java.security.SecurityPermission
- java.security.UnresolvedPermission
- java.awt.AWTPermission
- java.io.FilePermission
- java.io.SerializablePermission
- java.lang.reflect.ReflectPermission
- java.lang.RuntimePermission
- java.net.NetPermission
- java.net.SocketPermission
- java.sql.SQLPermission
- java.util.PropertyPermission
- java.util.logging.LoggingPermission
- javax.net.ssl.SSLPermission
- javax.security.auth.AuthPermission
- javax.security.auth.PrivateCredentialPermission
- javax.security.auth.kerberos.DelegationPermission
- javax.security.auth.kerberos.ServicePermission
- javax.sound.sampled.AudioPermission
More details about the Java security permissions are available here.
The OSGi specification defines a few custom permissions, that can be grouped by the layers of the OSGi framework.
Layer | Permission(s) |
---|---|
Module | PackagePermission - Controls which packages a bundle is allowed to import and/or export BundlePermission - Controls which bundles a bundle is allowed to require |
Lifecycle | AdminPermission - Controls which bundles are allowed to perform sensitive lifecycle operations |
Services | ServicePermission - Controls which services a bundle is allowed to publish and/or use |
All the OSGi-specific permissions follow the same pattern of name and actions used by Java. For further details on the OSGi permissions, please refer to the official OSGi documentation.
Policy Generation
Prevent External Malicious Attacks
Custom security policies should be created in order to prevent external malicious attacks from unsigned bundles that can be installed on field devices. The policy should be as strict as possible in order to reduce the effects of malicious bundles on targeted system(s).
This Eurotech security policy example may be downloaded and used as a starting-point for the creation of custom policies:
Eurotech recommends creating security policies that only allow the execution of signed bundles.
Policy Change
The security policy file currently used by ESF is available at /opt/eurotech/esf/kura/security.policy
To change the current security policy the steps to do are (with ESF running):
- overwrite the current policy file at /opt/eurotech/esf/kura/security.policy with the new security policy file;
- using the ESF Gateway Administration Console, the policy file fingerprint must be reloaded (more info below). This feature is available by selecting the Settings option in the Systems area of the console and selecting the Security Options tab, and then clicking on the Reload fingerprint button provided in the Security Policy area;
- stop and restart ESF.
A backup copy of the default security policy is available at /opt/eurotech/esf/security/policy_backup/
Consistency Check
During boot initialization, ESF validates the security policy file. In order to be accepted, the file must be compliant with the following XML Schema:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://eurotech.com/esf/2.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="security">
<xs:complexType>
<xs:sequence>
<xs:element name="properties">
<xs:complexType>
<xs:sequence>
<xs:element name="property">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:string" name="name"/>
<xs:element type="xs:string" name="value"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="policies">
<xs:complexType>
<xs:sequence>
<xs:element name="policy" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:string" name="access"/>
<xs:element name="conditions" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="condition">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:string" name="name"/>
<xs:element type="xs:string" name="value"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="permissions">
<xs:complexType>
<xs:sequence>
<xs:element name="permission" maxOccurs="unbounded" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:string" name="name"/>
<xs:element name="values">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:string" name="value" maxOccurs="unbounded" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element type="xs:string" name="name"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute type="xs:byte" name="version"/>
</xs:complexType>
</xs:element>
</xs:schema>
If errors are detected, an exception is thrown and the framework shuts down.
It is recommended that the security policy file is verified against the provided XML Schema. Linux systems may use xmllint for this purpose as shown below.
xmllint --schema <xml schema>.xsd <security policy file>.xml
Switch to Production Mode
Switching to production mode can be tricky if steps are not executed in the proper order and might require reinstalling ESF. Among the possible methods the safest is:
- Shudown ESF
# Note: on Linux distribution based on System V init scripts, the ESF process
# is monitored by Monit which will restart ESF is this is stopped.
/etc/init.d/monit stop
/etc/init.d/kura stop
- Replace the default /opt/eurotech/esf/kura/security.policy with one suitable for production mode (see above). At a minimum the value of the permit-debug property must be set to false
<esf:property>
<esf:name>permit-debug</esf:name>
<esf:value>false</esf:value>
</esf:property>
- Restart ESF
# Monit will restart ESF
/etc/init.d/monit start
Upon starting in production mode ESF automatically fingerprints the new security policy file and the command line of its process. Of course the method will also work straight after ESF installation, omitting the first step.
Startup Security Checks
In production mode ESF does the following security checks on startup:
-
Policy file fingerprint
-
ESF process command line fingerprint
-
Java Virtual Machine options
ESF will shutdown if any one of these checks fail.
Policy File Fingerprint
The policy file is fingerprinted the first time ESF starts in production mode and verified on every startup. If the security policy has to be changed, the user must fingerprint the security file again.
1.With ESF running edit /opt/eurotech/esf/kura/security.policy
2. Log in to the ESF Gateway Administration Console
3. Navigate to System | Settings. In the Security Options tab click the Reload fingerprint button of Security Policy.
FIXME: update screenshot as it shows Security Policy instead of Security Options
ESF Process Command Line Fingerprint
Like for the security policy file, the command line used to run the ESF Java process is fingerprinted the first time ESF starts in production mode and verified on every startup.
The command line can be found in the TBD ESF startup script for production mode.
If the command line has to be changed, the user must fingerprint the startup file again.
1.With ESF running edit TBD ESF startup script for production mode
2. Log in to the ESF Gateway Administration Console
3. Navigate to System | Settings. In the Security Options tab click the Reload fingerprint button of Startup command line.
FIXME: update screenshot as it shows Security Policy instead of Security Options
Java Virtual Machine Options
In production mode ESF checks various JVM options to protect against the use of profilers and debuggers that could disclose confidential information.
-
HeapDumpPath - is set to /dev/null
-
-Xdebug - is not defined
-
-Xrunjdwp - is not defined
-
-Xbootclasspath: - is not defined
-
-XX:StartFlightRecording - is not defined
-
-agentlib: - is not defined
-
-agentpath: - is not defined
-
-verbose - is not defined
ESF Gateway Administration Console (Web UI) Security Improvements
The ESF Gateway Administration Console features that prevent security breaches are described in the following sections.
Arbitrary File Write
The ESF Gateway Administration Console allows to execute shell scripts on the device directly from the Web UI, it is also possible to upload a ZIP file that will be extracted by the device to the working directory of the user script.
ESF sanitizes extracted file names to block directory traversal attacks and guard against security attacks through file uploads using custom and malicious ZIP files .
ESF protects against DOS attacks through file uploads by providing the ability to configure a maximum size of the extracted content of 100 MB and a maximum number of 1024 extracted files. These settings may be configured in the kura.properties file using the following parameters:
-
file.command.zip.max.size (size specified in MB)
-
file.command.zip.max.number
XSS on User and System Data
ESF sanitizes data to guard against XSS attacks and checks data that is fetched from the operating system.
Hidden Web Passwords
ESF prevents the transfer of password values to the client browser. This feature may also defend against brute force attacks based on knowledge of password size.
XSRF (Cross-site Request Forgeries) Attacks
ESF checks all Gateway Administration Console interactions using XSRF tokens. These single-use tokens are session related and are checked prior to every user interaction on the device side.
Bundle Signing
Before switching ESF to production mode application bundles developed by customers must be signed. ESF bundles are already signed by Eurotech and do not require to be signed by the customer.
Certificate Generation
Public Key Infrastructure (PKI) certificates can be created in different ways:
-
Self-signing the public key (self-signed certificate)
-
Signing the public key with a company's certificate chain
-
Sending a Certificate Signing Request (CSR) to a trusted Certificate Authority (CA)
For code signing the first two options are inexpensive. Most of the times a self-signed certificate is just enough to get-started.
Self-signed Certificate
A public/private key-pair and the associated self-signed certificate can be created using the keytool command-line utility. This utility is available, free of charge, from any Java Development Kit (Oracle JDK, OpenJDK etc) for all operating systems.
The example below makes use of ${} to denote a placeholder that will be replaced by an actual value. The notation is the same used in shell scripts to expand the variable between braces.
For example ${keystore_file} can be replaced by BundleSigningKeystore.ks.Please note that the generated keystore contains the private key used to sign your code.
The keystore and the private key are protected by passwords (see below).
Keep these passwords and the keystore in a safe place.
- Use keytool to generate a keystore containing the new pubic/private key-pair and the associated self-signed certificate:
# Note: key_pass and keystore_pass are respectively the password of the keystore
# and the password of the private key.
# Keep these passwords and the keystore in a safe place.
keytool -genkey -noprompt -trustcacerts -keyalg RSA -keysize 2048 \
-alias ${cert_alias} -dname ${dn_name} -keypass ${key_pass} \
-keystore ${keystore_file} -storepass ${keystore_pass} \
-validity ${cert_validity_days}
where:
- ${cert_alias} is the alias for the new certificate.
- ${dn_name} is the Distinguished Name of the new certificate (see below).
- ${key_pass} is the password to be set for the new certificate.
- ${keystore_file} is the filename of the new keystore.
- ${keystore_pass} is the password to be set for the new keystore.
A valid Distinguished Name (dn_name) can be the following:
CN=www.companyname.com,O=Company Name,C=USCommas (,), quotation marks (") and semicolons (;) appearing in the certificate Distinguished Name must be escaped using the backlash (\) character.
- Optionally check that the newly created certificate is in the keystore:
keytool -list -v -alias ${cert_alias} -keystore ${keystore_file}
Import the Certificate in ESF
The certificate that will be used to verify the bundles signature must be first imported in the ESF keystore. The location of the ESF keystore in the gateway filesystem is /opt/eurotech/esf/kura/certificates.ks. There are two ways the bundle signing certificate can be added to ESF:
- At any time from the Settings menu of the Everyware Gateway Administration Console Web UI
- Before the very first ESF boot adding it manually to the ESF keystore.
Continuing the example of the previous section, the bundle signing certificate must be first exported by the bundle signing keystore:
# Note: the certificate will be exported to the file denoted by cert.file.
# The certificate is public and does not contain any secret.
# You will need the password of the keystore and the password of the private key.
keytool -export -v -keystore ${keystore_file} -alias ${cert_alias} \
-file ${cert_file} -storepass ${keystore_pass} -keypass ${key_pass}
Import the Certificate from the Administration Console
This method can be used at any time to quickly add a bundle signing certificate to ESF.
The certificate exported in the previous section is in DER format while the Administration Console only accepts certificates in PEM format.
The exported certificate can be converted using the openssl utility:
openssl x509 -inform der -in ${cert_file} -out ${cert_file_pem}
The certificate (in PEM format) can then be added to the ESF keystore from Settings | Application Certificate in the Administration Console:
Import the Certificate Directly in the ESF Keystore
The bundle signing certificate can be directly imported in a keystore that will replace the ESF factory keystore located at /opt/eurotech/esf/kura/certificates.ks on the gateway filesystem.
This can be done only before the very first ESF boot regardless whether ESF is running in development or production mode.
If the ESF keystore located at /opt/eurotech/esf/kura/certificates.ks is replaced after the very first boot, ESF will detect that the keystore has been tampered and will refuse to boot.For security reasons the ESF factory keystore located at /opt/eurotech/esf/kura/certificates.ks is protected by an initial password which is not disclosed to customers. For this reason customers cannot directly import their certificates to this keystore.
Eurotech provides an equivalent keystore, whose password is defaultPassword, containing the Eurotech certificates needed to verify the signature of the ESF runtime.
This keystore can be downloaded from this link.The customer can import his own certificates in this keystore and replace the ESF factory keystore located in /opt/eurotech/esf/kura/certificates.ks.
Please be aware that the value of the initial keystore password is written in plain text in the value of the kura.keystore.password property in /opt/eurotech/esf/kura/kura.properties. For improved security it is advisable to change the initial keystore password and modify the kura.keystore.password accordingly.
Having the keystore password stored in plain text in the kura.properties file represents a momentary vulnerability where the keystore could be tampered by an attacker before the very first ESF boot. For improved security it is advisable to boot ESF once before shipping the gateway.
In all cases ESF will change the initial keystore password to an randomly generated password at the very first boot. The new password is securely stored and cannot be recovered.
Continuing the example, the certificate exported from the the bundle signing keystore can be imported in another keystore with:
# Note: certificates.ks is the name of the keystore downloadable from
# https://s3.amazonaws.com/esf-resources/certificates.ks
# that will replace /opt/eurotech/esf/kura/certificates.ks
keytool -import -alias ${cert_alias} -keystore certificates.ks -file ${cert_file}
Update the ESF Security Policy
After adding the bundle signing certificate(s) to ESF, the security policy file has to be updated accordingly to allow the execution of genuine bundles.
Continuing the example, the new rule to be applied is the following:
<esf:policy>
<esf:access>ALLOW</esf:access>
<esf:conditions>
<esf:condition>
<esf:name>
BundleSignerCondition
</esf:name>
<esf:value>
${dn.name}
</esf:value>
</esf:condition>
</esf:conditions>
<esf:permissions>
<esf:permission>
<esf:name>java.security.AllPermission</esf:name>
<esf:values>
<esf:value>
*
</esf:value>
<esf:value>
*
</esf:value>
</esf:values>
</esf:permission>
</esf:permissions>
<esf:name>
All permissions to bundles signed with this certificate
</esf:name>
</esf:policy>
As throughout the example, in the above XML snippet the placeholder ${dn_name} must be replaced with the actual Distinguished Name, for example "CN=www.companyname.com,O=Company Name,C=US".
JAR Signing
Bundles can be signed in the following ways:
-
Using the command line
-
Using Eclipse
-
Using Maven during the build process
Command Line Signature
To sign bundles from the command line, use the jarsigner command:
jarsigner -keystore file:${keystore_file} -tsa http://timestamp.digicert.com \
-storepass ${keystore_pass} -keypass ${key_pass} ${bundle_jar} ${cert_alias}
To verify the signature, use the following command:
jarsigner -verify -keystore file:${keystore_file} ${bundle_jar}
Bundle signature with Eclipse IDE
The Eclipse IDE provides bundle signing signature feature, abstracting jarsigner.
A simple guide on how to export bundle plugins and how to sign them is available here.
Updated less than a minute ago