- System Requirements
- Docker Commands
- Podman Commands
- Configuring Proxies
- Configuring Ondie
- Configuring Log Messages
- Running Demo
- ServiceInfo Setup
- Enabling ServiceInfo transfer
- Customize for multi machine setup
- Enabling Access to the embedded database
- Certificate Validity Checks
- Support for multiple Databases
-
Host Operating System: Ubuntu (20.04, 22.04) / RHEL (8.6, 8.8) / Debian 11.4
-
Linux packages:
-
Docker engine (minimum 20.10.10, Supported till version 20.10.21) / Podman engine (For RHEL) 3.4.2+**
-
Docker-compose (minimum version 1.29.2) / Podman-compose 1.0.3(For RHEL)**
-
Haveged
-
Java 17
-
Maven (minimum 3.6.3)
-
NOTE: sudo
can be removed for docker commands to instill the principle of least privilege by adding a user to docker group in Ubuntu REFER.
- Use the following command to start the docker container.
sudo docker-compose up -d --build
- Use the following command to stop a specific docker container.
sudo docker stop <container-name>
OR
sudo docker stop <container-id>
- Use the following command to stop all running docker containers.
sudo docker stop $(sudo docker ps -a -q)
- Use the following command to remove a specific container.
sudo docker rm <container-name>
OR
sudo docker rm <container-id>
- Use the following command to remove the docker image.
sudo docker rmi <image-name>
OR
sudo docker rmi <image-id>
- Use the following command to delete all the docker artifacts. (Note: Docker containers must be stopped before deleting them)
sudo docker system prune -a
- Use the following command to start the podman container.
sudo podman-compose up -d --build
- Use the following command to stop a specific podman container.
sudo podman stop <container-name>
OR
sudo podman stop <container-id>
- Use the following command to stop all running podman containers.
sudo podman stop -a
- Use the following command to remove a specific container.
sudo podman rm <container-name>
OR
sudo podman rm <container-id>
- Use the following command to remove the podman image.
sudo podman rmi <image-name>
OR
sudo podman rmi <image-id>
- Use the following command to delete all the podman artifacts. (Note: podman containers must be stopped before deleting them)
sudo podman system prune -a
Follow the steps in hyperlink for complete proxy setup. REFER
For basic proxy setup, update the proxy information in _JAVA_OPTIONS
as
_JAVA_OPTIONS=-Dhttp.proxyHost=http_proxy_host -Dhttp.proxyPort=http_proxy_port -Dhttps.proxyHost=https_proxy_host -Dhttps.proxyPort=https_proxy_port -Dhttp.nonProxyHosts=non_proxy_host
where
http_proxy_host
: Represents the HTTP proxy hostname. Typically, it is an IP address or domain name in the proxy URL.
http_proxy_port
: Represents the HTTP proxy port. Typically, it is the port number in the proxy URL.
https_proxy_host
: Represents the HTTPS proxy hostname. Typically, it is an IP address or domain name in the proxy URL.
https_proxy_port
: Represents the HTTPS proxy port. Typically, it is the port number in the proxy URL.
non_proxy_host
: List of non-proxy hosts values separated by |
symbol.
Specify the combination of the hostname and the port information together for either HTTP, HTTPS, or both. For example, if the HTTP proxy is 'http://myproxy.com:900', then the following updates will be made to the properties:
http_proxy_host: myproxy.com
http_proxy_port: 900
non_proxy_host: myproxy.com | ip
NOTE: For RHEL, proxy variables need to be configured in /etc/environment
.
If no proxy needs to be specified, do not add these properties to your _JAVA_OPTIONS.
OnDie is a type of device with device RoT added during manufacturing. To support OnDie devices you will need to import the OnDie certificates into the database via the api on both the Manufacturer and Owner components.
In Ubuntu/RHEL, copy the csme.zip/csme-debug.zip file present in <fdo-pri-src>/component-samples/demo/aio
to Manufacturer & Owner docker containers manually or by adding the below line to Dockerfile/Podmanfile of Manufacturer & Owner :
cp <fdo-pri-src>/component-samples/demo/aio/csme.zip <fdo-pri-src>/component-samples/demo/manufacturer
cp <fdo-pri-src>/component-samples/demo/aio/csme.zip <fdo-pri-src>/component-samples/demo/owner
COPY ./csme.zip ./csme.zip/
Restart Docker/Podman containers and execute the below CURL command to upload Ondie artifacts to DB.
- `POST /api/v1/ondie` to upload OnDie certificates into the database.
- Ex: `(curl -D - --digest -u ${api_user}:${api_passwd} --location --request POST "http://${ip}:{port}/api/v1/ondie" --header 'Content-Type: text/plain' --data-raw "${cert-file}" `
where ip = ip address of the manufacturer or the owner
port = port of the manufacturer or the owner
cert-file = zip file containing the OnDie certificates (this file is located in the component-samples/demo/aio folder). If using any pre-prod devices then import the csme-debug.zip file. Otherwise, for production devices, import the csme.zip file. Example file reference on Windows: "file:///C:/FDO/component-samples/demo/aio/csme-debug.zip" and on Ubuntu/RHEL : "file:///{path within docker container}/csme.zip".
The components use LOG4J2 for logging. The logging configuration can be updated through log4j2.xml file stored within each component folder. By default, INFO logs are prints on the console and DEBUG logs are printed in the log file.
Use following command to extract the log file from a running container.
docker container cp <container-id>:/home/fdo/app-data/service.log .
NOTE: Use the following commands to enable FDO support on RHEL.
cd <fdo-pri-src>/component-samples/demo/scripts
bash scripts/enable_podman_support.sh
grep -qxF 'export PODMAN_USERNS=keep-id' ~/.bashrc || echo $'\nexport PODMAN_USERNS=keep-id' >> ~/.bashrc
source ~/.bashrc
NOTE: Before starting the containers on RHEL, assign your machine IP to host.docker.internal in "/etc/hosts" and replace "host.docker.internal:host-gateway" with "host.docker.internal:your-machine-IP" from the extra_hosts field in the docker-compose.yml file for each component.
-
Start the FDO Manufacturer Sample as per the steps outlined in Manufacturer README.
-
Complete Device Initialization (DI) by starting the FDO HTTP Java Device Sample as per the steps outlined in Device README. Delete any existing 'credentials.bin' before starting the device.
-
Start the PRI RV Sample as per the steps outlined in RV README.
-
Start the PRI Owner Sample as per the steps outlined in Owner README.
-
Complete Ownership Voucher Extension by using the APIs
-
GET /api/v1/certificate?alias=<attestation-type>
to collect Owner's certificate.- Ex:
curl -D - --digest -u ${api_user}: --location --request GET 'http://${owner_ip}:8042/api/v1/certificate?alias=SECP256R1'
- Ex:
-
POST /api/v1/mfg/vouchers/<serial_no>
to collect voucher.- Ex:
curl -D - --digest -u ${api_user}:${api_passwd} --location --request POST "http://${mfg_ip}:8039/api/v1/mfg/vouchers/${serial_no}" --header 'Content-Type: text/plain' --data-raw "$owner_certificate" -o ${serial_no}_voucher.txt
- Ex:
-
POST /api/v1/owner/vouchers/
to upload ownership voucher to Owner.- Ex:
curl -D - --digest -u ${api_user}:${api_passwd} --location --request POST "http://${owner_ip}:8042/api/v1/owner/vouchers/" --header 'Content-Type: text/plain' --data-raw "$extended_voucher" -o ${serial_no}_guid.txt
- Ex:
-
GET /api/v1/to0/${device_guid}
to initiate TO0- Ex:
curl -D - --digest -u ${api_user}:${api_passwd} --location --request GET "http://${owner_ip}:8042/api/v1/to0/${device_guid}"
- Ex:
-
Or Extend and upload ownership voucher using
extend_upload.sh
script present in<fdo-pri-src>/component-samples/demo/scripts
- Ex:
bash extend_upload.sh -m ${mfg_ip} -o ${owner_ip} -s abcdef
- Ex:
-
Please refer to PRI Manufacturer REST APIs for more information about the API. Optionally, if ServiceInfo transfer is needed, please refer to Enabling ServiceInfo transfer.
-
-
Complete Transfer Ownership 1 and 2 (TO1 and TO2) by starting the FDO PRI HTTP Java Device Sample again. The previously created 'credentials.bin' from Step#2 will be used directly by the Device.
NOTE: Credential Reuse protocol is enabled by default, that is, after a successful onboarding the device credentials remain unchanged.
The FDO PRI HTTP Java Device Sample currently supports fdo_sys
and FSIM
module for interpreting received owner ServiceInfo and devmod
module to share device ServiceInfo with Owner.
-
fdo_sys
Owner ServiceInfo module: This module supports the following 4 message names as listed below to interpret the ServiceInfo as received from the Owner. The basic functionality of this module is to support payload/script transfers and basic command execution. A sample format looks like 'fdo_sys:filedesc=filename, fdo_sys:write=filecontent,fdo_sys:exec=command-to-execute,fdo_sys:exec_cb=command-to-execute'.filedesc - The name to be given to the file once it is transferred. Upon receiving this, device creates a file with the given name and opens stream to write into it.
write - The payload/content (script, binaries, and others) that is sent to the device. Upon receiving this, device writes the content into the open stream as given by the preceding 'filedesc' message.
exec - The command that will be executed at the device. Device executes the command as received.
exec-cb - This command returns the status / progress of the process running.
fetch - This command returns the fetched file from client filesystem.
- The fetched file is stored in
owner/app-data/<device_guid>
owner_exec - This command is a generic execution module that can perform various tasks, such as downloading files, running scripts, and executing commands. For executing specific package add the package in the respective Dockers.
Sample SVI instruction :
{"filedesc" : "sample.txt","resource" : "http/https url"}, {"exec" :"cat sample.txt"}
or
{"filedesc" : "sample.txt","resource" : "database resource"}, {"exec" :"cat sample.txt"}
Sample SVI instruction (with owner_exec):
{ "owner_exec" : ["curl", "—location —digest", "-U username: -P:xyz", "https://sample:9999/files"] }
- The fetched file is stored in
NOTE: The comma-separated values must be ordered such that the 'filedesc' and 'write' objects are one after the other pair-wise, followed by the 'exec' commands.
NOTE: You can filter SVI transfer based on Device parameters. Learn more
devmod
Device ServiceInfo module: This module supports multiple messages as listed down in the protocol specificationREFER, that is sent to the Owner as Device ServiceInfo. A sample format looks like 'devmod:active=1'.
The FDO PRI Owner Sample currently supports fdo_sys
and FSIM
modules for sending serviceInfo to the PRI Device and devmod
module to store the received Device ServiceInfo. When device information is inserted into the database table 'ONBOARDING_CONFIG', it'll not have any association with the ServiceInfo values, and so by default, no ServiceInfo is transferred to the Device.
The FDO PRI Owner and PRI Java Device currently supports FSIM modules: fdo.download
, fdo.command
.
fdo.download - sends payload/content (script, binaries, and others) from Owner to the device. Upon receiving this, device writes the content into the open stream as given by the preceding 'filedesc' message.
fdo.command - The command that will be executed at the device. Device executes the command as received.
Activate the following workers in Device's service.yml
:
- org.fidoalliance.fdo.sample.FdoSimDownloadDeviceModule
- org.fidoalliance.fdo.sample.FdoSimCommandDeviceModule
and Activate the following workers in Owner's service.yml
- org.fidoalliance.fdo.sample.FdoSimDownloadOwnerModule
- org.fidoalliance.fdo.sample.FdoSimCommandOwnerModule
[{"filedesc" : "script.bat","resource" : "script.bat", "module":"fdo.download"}]
or
[{"filedesc" : "script.bat","resource" : "script.bat", "module":"fdo.download"}, {"exec_cb" : ["cmd", "/c", "script.bat"], "module":"fdo.command"}]
NOTE: Currently, FDO_SYS and FSIM modules cannot be used simultaneously. Disable FSIM modules if you are using FDO_SYS. You can filter SVI transfer based on Device parameters. Learn more
Generic sample Digest authentication call:
curl -D - --digest -u ${apiUser}:${api_password} --location --request POST 'http://host.docker.internal:8039/api/v1/rvinfo' \
--header 'Content-Type: text/plain' \
--data-raw '[[[5,"{rv-dns}"],[3,8040],[12,1],[2,"{rv-ip}"],[4,8040]]]'
Replace -D - --digest -u ${apiUser}:${api_password}
with --cacert path-to-CA --cert path-to-client-Certificate
Finally, the mTLS enabled cURL request will be:
curl \
--cacert scripts/secrets/ca-cert.pem --cert scripts/api-user.pem \
--location --request POST 'http://host.docker.internal:8039/api/v1/rvinfo' \
--header 'Content-Type: text/plain' \
--data-raw '[[[5,"{rv-dns}"],[3,8040],[12,1],[2,"{rv-ip}"],[4,8040]]]'
-
Copy the required Keystores into
demo/<fdo-component>/app-data
folder. -
Disable the following workers.
org.fidoalliance.fdo.protocol.db.StandardKeyStoreInputStream
org.fidoalliance.fdo.protocol.db.StandardKeyStoreOutputStream
- Enable the following workers.
org.fidoalliance.fdo.FileKeyStoreInputStream
org.fidoalliance.fdo.FileKeyStoreOutputStream
- Update
service.yml
with the new Keystore filename and credentials inservice.env
keystore:
path: new-keystore-filename
store-type: PKCS12
password: $(encrypt_password)
To enable ServiceInfo transfer to a Device with a given GUID, follow the steps below:
Insert required ServiceInfo resources into the database table 'SYSTEM_RESOURCE' using the API POST /api/v1/owner/resource?filename=fileName
. More information about the same is provided in section FDO PRI Owner REST APIs. If the required ServiceInfo already exists in the table with appropriate tags, start TO1.
curl -D - --digest -u apiUser:${api_password} --location --request POST 'http://{owner_ip}:{owner_port}/api/v1/owner/resource?filename=<file-name>'
--header 'Content-Type: text/plain' --data-binary '@<path-to-file>'
NOTE: In case of transferring large payload file via ServiceInfo, there is a possibility that runnning container might hit memory limit set in the respective docker-compose.yml file.
This can be confirmed using the docker stats
command that shows live resource statistics for running containers.
During SVI Transfer, observe the column MEM USAGE / LIMIT
for respective container when SVI transmission is in progress.
If the failure is observed when MEM USAGE reaches the LIMIT, it is due to the memory limit we have allocated for the container.
To resolve this, edit fields mem_limit
and mem_reservation
in the respective docker-compose.yml according to the needs and restart the container.
When connecting to the external signed resource, the application should be using the default java ca-certs instead of the custom truststore. The following properties in service.yml
need to be commented out to pick the default java ca-certs.
#javax.net.ssl.keyStorePassword: default
#javax.net.ssl.trustStorePassword: default
#javax.net.ssl.keyStore: $(secrets.path)/api-user.pem
#javax.net.ssl.trustStore: $(secrets.path)/ca-cert.pem
#javax.net.ssl.keyStoreType: PEM
#javax.net.ssl.trustStoreType: PEM
To support external signed resources along with self-signed resource, add the self-signed certificate to the default java ca-certs. Add following lines to the Dockerfile
of individual component.
COPY secrets/ca-cert.pem /home/fdo/ca-cert.pem
RUN keytool -import -trustcacerts -noprompt -keystore "/usr/lib/jvm/java-17-openjdk-amd64/lib/security/cacerts" -storepass changeit -alias tomcat -file /home/fdo/ca-cert.pem
While customizing the system configuration for multi machine setup, ensure that all host.docker.internal
and 172.17.0.1
references are replaced with respective DNS and IP address of the host machine. The required changes includes :
-
Updating RVInfo blob in Manufacturer
- Sample cURL command to update RVInfo in Manufacturer running in the host.docker.internal.
curl -D - --digest -u ${apiUser}:${api_password} --location --request POST 'http://host.docker.internal:8039/api/v1/rvinfo' \ --header 'Content-Type: text/plain' \ --data-raw '[[[5,"{rv-dns}"],[3,8040],[12,1],[2,"{rv-ip}"],[4,8040]]]'
- Sample cURL command to update RVInfo in Manufacturer running in the host.docker.internal.
-
Updating T02RVBlob in Owner
- Sample cURL command to update T02RVBlob in Owner running in the host.docker.internal.
curl -D - --digest -u ${apiUser}:${api_password} --location --request POST 'http://host.docker.internal:8042/api/v1/owner/redirect' \ --header 'Content-Type: text/plain' \ --data-raw '[["{owner-ip}","{owner-dns}",8042,3]]'
- Sample cURL command to update T02RVBlob in Owner running in the host.docker.internal.
-
Activate
org.fidoalliance.fdo.protocol.EmbeddedDatabaseServer
worker inservice.yml
file. -
Change hibernate properties in
service.yml
to connect with H2 database:hibernate-properties: connection.driver_class: org.h2.Driver hibernate.connection.username: $(db_user) hibernate.connection.password: $(db_password) hibernate.connection.url: jdbc:h2:tcp://localhost:9092/./app-data/emdb hibernate.dialect: org.hibernate.dialect.H2Dialect
-
Append the following
h2-database
configuration toservice.yml
file.h2-database: tcp-server: - "-ifNotExists" web-server: - "-webPort" - "8082"
-
In
hibernate.cfg.yml
, ensure that propertyhibernate.connection.driver_class
is mapped toorg.h2.Driver
.
NOTE: It is recommended to configure H2 DB with the default settings for properties like webAllowOthers, either in a config file or as a parameter passed in, when the servlet is invoked
For out of the box demo purposes, FDO services are configured to trust self-signed certificates.
In production environments, the configurators should disable the trust for these self-signed certificates by updating the worker list in service.yml
file of respective component.
Sample service.yml
file:
Disable the following workers
#- org.fidoalliance.fdo.protocol.SelfSignedHttpClientSupplier
#- org.fidoalliance.fdo.protocol.db.BasicServiceInfoClientSupplier
and enable
- org.fidoalliance.fdo.protocol.StandardHttpClientSupplier
- org.fidoalliance.fdo.protocol.db.StandardServiceInfoClientSupplier
The Device certificate chain is checked during TO0 by the rendezvous server and by the owner during TO2. This check includes verifying the certificate chain hash and if the leaf certificate has expired. Owner certificates with X5Chain encoding are not analyzed by this implementation.
The FDO spec states "When processing an Ownership Voucher with X5CHAIN encoding, the Rendezvous Server and Device SHOULD verify the certificate chain as much as possible, and MAY decide to accept or reject the Ownership Voucher based on a trust analysis of the X5CHAIN". REFER
This implementation will trust Ownership vouchers with X5Chain encoding without verifying the ownership chain.
FDO services support multiple databases for the storage layer. Hibernate framework is used to bridge the application layer with backend database storage layer. Out of the box, the following databases are supported by the FDO service:
- H2 Database
- MariaDB
- MySQL
- PostgreSQL
-
Follow the below steps to configure FDO services with MySQL database:
a. Make sure that
connection.driver_class
is commented out inhibernte.cfg.xml
.b. Update
hibernate-properties
inservice.yml
hibernate-properties: hibernate.connection.username: fdo hibernate.connection.password: sample_password hibernate.connection.driver_class: com.mysql.cj.jdbc.Driver hibernate.connection.url: jdbc:mysql://host.docker.internal:3306/fdo?createDatabaseIfNotExist=true&useSSL=false&allowPublicKeyRetrieval=true hibernate.dialect: org.hibernate.dialect.MySQLDialect hibernate.dialect.storage_engine: innodb hibernate.connection.requireSSL: false hibernate.connection.autoReconnect: true
c. Make sure that
useSSL
andrequireSSL
is set tofalse
inservice.env
file.d. Use the below command to start MySQL server
docker run -d -p 3306:3306 --name mysql-docker-container -e MYSQL_ROOT_PASSWORD=root-password -e MYSQL_DATABASE=fdo -e MYSQL_USER=sample-user -e MYSQL_PASSWORD=sample-password mysql/mysql-server:8.0.31
For RHEL environment,
sudo podman run -d -p 3306:3306 --name mysql-docker-container -e MYSQL_ROOT_PASSWORD=root-password -e MYSQL_DATABASE=fdo -e MYSQL_USER=sample-user -e MYSQL_PASSWORD=sample-password mysql/mysql-server:8.0.31
-
Follow the below steps to configure FDO services with PostgreSQL database:
a. Make sure that
connection.driver_class
is commented out inhibernte.cfg.xml
.b. Update
hibernate-properties
inservice.yml
hibernate-properties: hibernate.connection.username: fdo hibernate.connection.password: sample_password connection.driver_class: org.postgresql.Driver hibernate.connection.autoReconnect: true hibernate.connection.requireSSL: false hibernate.connection.url: jdbc:postgresql://host.docker.internal:5432/fdo?createDatabaseIfNotExist=true&useSSL=false hibernate.dialect: org.hibernate.dialect.PostgreSQLDialect
c. Make sure that
useSSL
andrequireSSL
is set tofalse
inservice.env
file.d. Use the below command to start PostgreSQL server
docker run -d --name postgres-db -e POSTGRES_ROOT_PASSWORD=root-password -e POSTGRES_DB=fdo -e POSTGRES_USER=sample-user -e POSTGRES_PASSWORD=sample-password -p 5432:5432 -d postgres
For RHEL environment,
sudo podman run -d --name postgres-db -e POSTGRES_ROOT_PASSWORD=root-password -e POSTGRES_DB=fdo -e POSTGRES_USER=sample-user -e POSTGRES_PASSWORD=sample-password -p 5432:5432 -d postgres
-
Follow the guide to configure FDO services with MariaDB. By default, FDO services are configured to support MariaDB.
-
Follow the Embedded DB guide to configure FDO services with H2 Database.