Security Best Practices¶
This how-to-guide describes security best practices for deploying EVerest in a production environment. It provides checklists with items to consider for securing the system.
EVerest-related security aspects¶
Do not expose the main MQTT broker on any interface (e.g. PLC, Ethernet, …). We recommend using a unix domain socket between EVerest and Mosquitto.
In Mosquitto, config file add the line:
listener 0 /run/mosquitto/mosquitto.sock
In the EVerest settings file passed to the manager with --settings, add:
mqtt_broker_socket_path: /run/mosquitto/mosquitto.sock
Access to the socket is controlled by the permissions of its directory and of
the socket itself, which Mosquitto creates with mode 0770 after dropping
privileges to the broker user. Use a directory that only the broker and the
user running EVerest can access, for example /run/mosquitto owned by the
broker user and its group, mode 0750, with the user running EVerest as a
member of that group. The systemd unit of Mosquitto 2.0 creates
/run/mosquitto with mode 0740, which keeps the group out. Do not place
the socket in a world-writable directory such as /tmp: any local user could
connect to it, or create a socket there before the broker does.
Files that restrict access to the broker, such as its acl_file or
password_file, should be owned by the broker user. Mosquitto 2.1 warns
about files with other owners and announces refusing them in a future version.
The installed example etc/everest/settings-mqtt-uds.yaml is a complete
settings file for this setup. The meta-everest Yocto layers configure
Mosquitto and everest.service this way, see
Building Yocto for your custom hardware.
If a part of an MQTT API should be exposed, e.g. to a mobile app on the same wireless LAN, use mutually authenticated TLS for the MQTT connection and run a separate Mosquitto instance. Bridge only those API topics strictly needed (and nothing from the “everest” topic) between the two Mosquitto instances. Shutdown the public Mosquitto whenever it is not needed.
Use OCPP security level 3 for the CSMS connection.
Use a hardware security module, e.g. TPM2 for generating and securing private keys.
Do not run EVerest modules as root user. Create a low privilege user, that has access only to what is needed (via filesystem permissions, group memberships, …). In Yocto, you can do this in your image file similar to this:
inherit useradd
USERADD_PACKAGES = "${PN}"
USERADD_PARAM:${PN} = "-u 1500 -d /home/everest -g everest -G systemd-journal,dialout,tty,gpio,tss,mosquitto -r -s /bin/false everest"
GROUPADD_PARAM:${PN} = "-g 1500 everest; -g 1501 gpio; -r systemd-journal; -r tss"
GROUPMEMS_PARAM:${PN} = ""
Note
Make sure to use the correct groups and settings for your system.
Start the manager as this user, the modules it forks then run as the same
user. With systemd, set User= and Group= in the unit, e.g. with a
drop-in for everest.service:
# /etc/systemd/system/everest.service.d/10-user.conf
[Service]
User=everest
Group=everest
The everest.service of the meta-everest layers starts the manager as
root, so add such a drop-in in a bbappend of your layer.
Do not set NoNewPrivileges=yes in the unit, and do not limit
CapabilityBoundingSet= below the capabilities the modules need,
otherwise the file capabilities described below have no effect.
Note
The run_as_user setting in the settings section of the EVerest
config file, with which a manager started as root switches itself and
the modules to another user, is deprecated (see
Active Deprecation Index). It still works, and the manager logs
a warning at startup when it is set.
Some modules need elevated privileges, e.g. SLAC module. Those modules
should be given individual Linux capabilities via file based capabilities.
A module declares the capabilities it needs in the capabilities list
of its manifest.yaml:
capabilities:
- CAP_NET_RAW
Here is a list of capabilities required by the EVerest modules:
Module name |
Capabilities |
|---|---|
EvseSlac / EvSlac |
CAP_NET_RAW |
Setup |
CAP_NET_ADMIN, CAP_NET_RAW, CAP_DAC_OVERRIDE |
PacketSniffer |
CAP_NET_RAW |
Huawei_V100R023C10 |
CAP_NET_RAW |
EvseManager |
CAP_NET_RAW, only with |
The script applications/utils/scripts/set_module_capabilities.py reads
the manifests of all installed modules and grants these capabilities to the
module binaries with setcap. It needs PyYAML, and setcap requires
root privileges (CAP_SETFCAP):
sudo python3 applications/utils/scripts/set_module_capabilities.py \
--modules-dir /usr/libexec/everest/modules
Add --dry-run to only print the setcap commands. Only compiled
modules (C++, Rust) can be given file capabilities. Python and JavaScript
modules are skipped with a warning, since capabilities on the interpreter
would apply to every script it runs.
The everest-core recipes of the meta-everest layers inherit the
everest_file_capabilities class, which runs this script in
pkg_postinst when the root filesystem is created, so images built with
them contain the file capabilities without further configuration.
Installing the package on a running target does not apply them, run the
script manually in this case.
Run the script again whenever module binaries are replaced, since
overwriting a file drops its capabilities. File capabilities are ignored
on file systems mounted with nosuid.
At startup, the framework adds these capabilities to the ambient set of
the module process, so programs it starts, e.g. ip called by the Setup
module, run with the same capabilities.
Note
Setting Linux capabilities for a module with the capabilities key
in the EVerest config file is no longer supported. The key is still
accepted, but the manager ignores it and logs a warning at startup.
The key is deprecated and will be removed (see
Active Deprecation Index).
To ensure that internal services cannot be accessed via the powerline connection, iptables can be used with the following rules. In this example the powerline module is on device seth0.
ip6tables -S | grep seth0
-A INPUT -i seth0 -p ipv6-icmp -j ACCEPT
-A INPUT -i seth0 -p udp -m udp --dport 15118 -j ACCEPT
-A INPUT -i seth0 -p tcp -m tcp --dport 50000 -j ACCEPT
-A INPUT -i seth0 -p tcp -m tcp --dport 61341 -j ACCEPT
-A INPUT -i seth0 -p tcp -m tcp --dport 64109 -j ACCEPT
-A OUTPUT -o seth0 -p ipv6-icmp -j ACCEPT
-A OUTPUT -o seth0 -p udp -m udp --sport 15118 -j ACCEPT
-A OUTPUT -o seth0 -p tcp -m tcp --sport 50000 -j ACCEPT
-A OUTPUT -o seth0 -p tcp -m tcp --sport 61341 -j ACCEPT
-A OUTPUT -o seth0 -p tcp -m tcp --sport 64109 -j ACCEPT
According to the standard, port 15118 is used for SDP messages.
Evse15118D20 integrates libiso15118 and uses the configured
tcp_port value for TCP and TLS1.2/1.3. The default is 50000.
The deprecated EvseV2G module uses the configurable tcp_port and
tls_port values (defaults: TCP 61341, TLS 64109); the rules for 61341 and 64109 above are only
needed while it is still in use. The deprecated IsoMux module listens on
TCP 61342 and TLS 64110 and forwards to the local EvseV2G and Evse15118D20 instances.
When changing these values, update the firewall rules accordingly.
General (non-EVerest-related) security aspects¶
Reaching complete system-level security is a complex topic, that should be handled by experts. This manual cannot give real advice here.
Following, we created an unordered and incomplete list of things that you should consider during the development process:
Use secure boot and make sure that you do a full lock-down on production units that prevents any unsigned code to boot.
Disable bootloader console on production units.
Disable any UART console login.
Disable or lock down all programmer interfaces such as SWD or JTAG in production units.
Don’t expose USB ports unless you really know what you are doing. A quite common attack vector for charging stations is to plug in a USB dongle and reconfigure / update the firmware - or attach a keyboard …
Evaluate if the rootfs or special partitions need encryption.
Use a secure vault for all private keys on the device (e.g. TPM2.0).
Make sure your PKI for secure boot and OTA is properly maintained. If you loose the private root key, the hardware in the field is lost.
Use signed updates. Evaluate if encryption is needed.
Use signed updates also for all other components, such as the safety MCU.
Be able to OTA-update all components in the charger through the normal OTA package - also third party components if they have a firmware update feature.
Network interfaces: Verify that only services being absolutely necessary are accessible on a network interface.
Use mTLS with TPM if you have a custom cloud connection.
Lock down all maintenance / service / repair ports.
Maintain the complete Linux system and provide regular updates.
Don’t use default passwords for anything - or better: Don’t use passwords at all.
Use software and hardware watchdogs.
Authors: Cornelius Claussen