Enhance MAAS security¶
MAAS security depends on encryption, logging, and user access management. This document describes how to keep all of these in reliable working order.
Use TLS termination (MAAS 3.3+)¶
Learn more about TLS termination.
Configure TLS (3.3+)¶
Manage TLS settings in MAAS with config-tls.
usage: maas config-tls [-h] COMMAND ...
Configure MAAS Region TLS.
optional arguments:
-h, --help show this help message and exit
drill down:
COMMAND
enable Enable TLS and switch to a secured mode (https).
disable Disable TLS and switch to a non-secured mode (http).
the following arguments are required: COMMAND
Enable TLS¶
TLS requires both a private key and a corresponding X509 certificate, both in PEM format:
usage: maas config-tls enable [-h] [--cacert CACERT] [-p PORT] key cert
positional arguments:
key path to the private key
cert path to certificate in PEM format
optional arguments:
-h, --help show this help message and exit
--cacert CACERT path to CA certificates chain in PEM format (default: None)
-p PORT, --port PORT HTTPS port (default: 5443)
The default HTTPS port is 5443; customize this with the --port option. Specify the full certificate change (e.g. when using a non-self-signed certificate) using the --cacert option.
Manage TLS with HA¶
High availability requires that every MAAS instance use the same certificate. Create one certificate with multiple domain names or IP addresses, for example:
X509v3 Subject Alternative Name:
DNS:example.com, IP Address:10.211.55.9
Disable TLS¶
When you disable TLS, MAAS API and UI will use HTTP over port 5240:
usage: maas config-tls disable [-h]
optional arguments:
-h, --help show this help message and exit
Check TLS status¶
Confirm TLS is active at Settings > Configuration > Security:
CN
Expiration date
Fingerprint
Certificate
This section of the UI will warn you if TLS is disabled.
Login to the MAAS CLI with TLS¶
Login to the MAAS API with an https URL when using TLS:
maas login <profile_name> https://mymaas:5443/MAAS <api_key>
usage: maas login [-h] [--cacerts CACERTS] [-k] profile-name url [credentials]
Log in to a remote API, and remember its description and credentials.
positional arguments:
profile-name The name with which you will later refer to this remote server and credentials within this tool.
url The URL of the remote API, e.g. http://example.com/MAAS/ or http://example.com/MAAS/api/2.0/ if you wish to specify the API
version.
credentials The credentials, also known as the API key, for the remote MAAS server. These can be found in the user preferences page in
the web UI; they take the form of a long random-looking string composed of three parts, separated by colons.
optional arguments:
-h, --help show this help message and exit
--cacerts CACERTS Certificate CA file in PEM format
-k, --insecure Disable SSL certificate check
If credentials are not provided on the command-line, they will be prompted for interactively.
the following arguments are required: profile-name, url
Certificates provided via --cacerts will be stored in your profile for future CLI commands.
Renew certificates¶
Renew a certificate the same way you enable TLS:
sudo maas config-tls enable new-server-key.pem new-server.pem --port 5443
Place the certificate and key in an accessible directory, such as /var/snap/maas/common if you’re using the MAAS snap.
Use certbot¶
certbot can be used to renew certificates, using a post-renewal hook to update MAAS:
#!/bin/bash -e
DOMAIN="maas.internal"
CERTSDIR="/etc/letsencrypt/live/$DOMAIN"
cd /var/snap/maas/common
# need to copy certs where the snap can read them
cp "$CERTSDIR"/{privkey,cert,chain}.pem .
yes | maas config-tls enable privkey.pem cert.pem --cacert chain.pem --port 5443
# we don’t want to keep private key and certs around
rm {privkey,cert,chain}.pem
Don’t forget to make the script executable:
chmod +x /etc/letsencrypt/renewal-hooks/post/001-update-maas.sh
When obtaining a new certificate.
sudo REQUESTS_CA_BUNDLE=ca.pem certbot certonly --standalone -d maas.internal --server https://ca.internal/acme/acme/directory
Note that hooks are run only on renewal. You can test the process with a --dry-run flag:
sudo REQUESTS_CA_BUNDLE=ca.pem certbot renew --standalone --server https://ca.internal/acme/acme/directory --dry-run
Refer to the certbot documentation for more information.
Manage PEM files¶
Combine SSL certificate (mysite.com.crt) and key pair (mysite.com.key) into a single PEM file:
cat mysite.com.crt mysite.com.key > mysite.com.pem
sudo cp mysite.com.pem /etc/ssl/private/
Include your root and intermediate CA certificates in the same PEM file, if required.
Activate MAAS hardening¶
MAAS hardening enforces STIG/CIS transport-security controls on the region controller. It is best-effort: when a prerequisite is missing, MAAS keeps running and reports the problem as a non-dismissable admin notification rather than refusing to start.
This section covers hardening the MAAS controllers. To deploy a FIPS kernel to a managed machine, see Deploy a FIPS kernel instead.
For the full parameter, store, and violation-code reference, see Security hardening reference.
Enable FIPS on the host¶
FIPS mode is a host kernel feature provided by Ubuntu Pro; it is independent of MAAS hardening. When FIPS mode is active, MAAS hardening activates automatically and cannot be turned off.
MAAS runs on the core24 snap base, which provides the cryptographic libraries
used at runtime. For FIPS-validated cryptography to apply inside the snap,
core24 must be switched to its FIPS-updates channel before the reboot.
Note: Ubuntu 24.04 LTS FIPS certification is in progress. The
fips-updates/stablechannel is not yet available; usefips-updates/candidateuntil certification is complete. Check the current certification status.
sudo pro attach <ubuntu_pro_token>
sudo pro enable fips-updates
sudo snap refresh core24 --channel=fips-updates/candidate
sudo reboot
After the reboot, confirm FIPS mode is active:
cat /proc/sys/crypto/fips_enabled
A value of 1 means FIPS mode is active.
Activate hardening on a non-FIPS host¶
Run maas config-hardening enable. This sets hardening_enabled=on in the
MAAS database; it is a pure database operation and does not touch
regiond.conf. Bind addresses are left unset by default; see Security
hardening reference
for the full derivation rule.
sudo maas config-hardening enable
Then restart MAAS to apply the change:
sudo snap restart maas
hardening_enabled accepts auto (default — active only when the host is in FIPS mode), on
(force active), or off (inactive; overridden by the host FIPS state). enable
is a shortcut for maas config-hardening set hardening_enabled on.
Set the hardening parameters¶
When hardening is active, MAAS validates transport-security prerequisites at
startup. Use maas config-hardening set to configure each parameter:
# api_bind is left unset by default: MAAS derives a specific address
# per address family from maas_url when hardening is active. Only set
# it explicitly to pin the public API to a different interface, or to
# bind several at once (comma-separated, may mix IPv4 and IPv6 -- any
# family not present in the explicit value is still auto-derived).
sudo maas config-hardening set api_bind 10.0.0.5
sudo maas config-hardening set api_bind 10.0.0.5,fd00::5
# Bind Prometheus metrics to loopback (already seeded by
# maas config-hardening enable; only needed if you skipped that step).
sudo maas config-hardening set prometheus_bind 127.0.0.1
# temporal_bind is left unset by default: MAAS derives it from maas_url.
# Only set it explicitly to pin Temporal to a different interface.
sudo maas config-hardening set temporal_bind 10.0.0.5
# rpc_bind is left unset by default: MAAS derives it from maas_url, the
# same as api_bind/temporal_bind. Set it explicitly (optionally as a
# comma-separated list) to pin exactly which address(es) racks dial.
sudo maas config-hardening set rpc_bind 10.0.0.5
# syslog_bind is left unset by default: MAAS derives it from maas_url, the
# same as rpc_bind/temporal_bind. Set it explicitly (optionally as a
# comma-separated list) to pin the syslog receiver to a different
# interface, e.g. when enrolled machines reach MAAS over a subnet other
# than the one maas_url resolves to.
sudo maas config-hardening set syslog_bind 10.0.0.5
# dns_bind has no maas_url-derived default: DNS must serve every
# managed subnet, not just the interface that reaches the API, so
# hardening always requires picking address(es) explicitly (may mix
# IPv4 and IPv6 in one comma-separated list). Snap installs only: MAAS
# owns the whole named.conf there; on Debian-packaged installs this key
# is not available (nor validated), since MAAS does not own the base
# named.conf.options.
sudo maas config-hardening set dns_bind 10.0.0.5,fd00::5
# Verify the PostgreSQL server certificate.
sudo maas config-hardening set database_sslmode verify-full
sudo maas config-hardening set database_sslcert /var/snap/maas/current/certs/db-client.pem
sudo maas config-hardening set database_sslkey /var/snap/maas/current/certs/db-client.key
sudo maas config-hardening set database_sslrootcert /var/snap/maas/current/certs/db-ca.pem
# Optional: a DH parameters file of at least 2048 bits.
sudo maas config-hardening set api_tls_dhparam /var/snap/maas/current/certs/dhparam.pem
The public-API TLS certificate and key are not hardening parameters —
configure them with maas config-tls enable. To generate DH
parameters, run openssl dhparam -out dhparam.pem 2048.
Verify the hardening posture¶
Check the effective values and their source stores:
maas config-hardening list
For a bind key that’s left unset but auto-derives from maas_url
(api_bind, agent_api_bind, http_proxy_bind, rpc_bind,
temporal_bind, syslog_bind), list appends the address(es) MAAS
would actually bind to right now, e.g. api_bind [conf ] (effective: 10.0.0.5,fd00::5). api_bind, agent_api_bind, and http_proxy_bind
show one address per family; rpc_bind, temporal_bind, and
syslog_bind show a single address matching whichever family
maas_url resolves to. prometheus_bind gets the same treatment but
with a loopback default instead of a maas_url-derived one. Nothing
is appended when the key is explicitly set for every derivable
family, or when no derivation is possible (e.g. hardening is inactive
for the keys that only derive under hardening).
Run validation on demand. It prints every violation and exits non-zero when any exist, so it doubles as audit evidence:
maas config-hardening validate
A clean run prints OK: no hardening violations.. Otherwise each violation lists its code, message, resolution, and the configuration key to fix. Correct the setting, restart MAAS, and the corresponding admin notification clears on the next startup.
Use TLS termination (3.2-)¶
MAAS versions 3.2 and below don’t support native TLS encryption. If you are not interested in setting up an HAProxy, you can still enable TLS.
Configure nginx¶
server {
listen 443 SQL;
server_name _;
ssl_certificate /etc/nginx/ssl/nginx.crt;
ssl_certificate_key /etc/nginx/ssl/nginx.key;
location / {
proxy_pass http://localhost:5240;
include /etc/nginx/proxy_params;
}
location /MAAS/ws {
proxy_pass http://localhost:5240/MAAS/ws;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
}
Note that MAAS binds to port 5240, not 80.
Configure apache2¶
<VirtualHost *:443>
SSLEngine On
SSLCertificateFile /etc/apache2/ssl/apache2.crt
SSLCertificateKeyFile /etc/apache2/ssl/apache2.key
RewriteEngine On
RewriteCond %{REQUEST_URI} ^/MAAS/ws [NC]
RewriteRule /(.*) ws://localhost:5240/MAAS/ws [P,L]
ProxyPreserveHost On
ProxyPass / http://localhost:5240/
ProxyPassReverse / http://localhost:5240/
</VirtualHost>
Manage network ports¶
Regulate accessible network ports for stronger MAAS security. Consider configuring your firewall to allow only the ports MAAS uses. Using the Ubuntu UncomplicatedFirewall:
sudo ufw enable
sudo ufw default deny incoming
sudo ufw allow 5240
sudo ufw allow 5248
sudo ufw allow 5241:5247/tcp
sudo ufw allow 5241:5247/udp
sudo ufw allow 5250:5270/tcp
sudo ufw allow 5250:5270/udp
Your specifics may vary, so refer to the relevant firewall documentation and the required MAAS controller port settings.
See MAAS network port reference table for a complete active port listing.
Deploy HAProxy¶
sudo apt-get update
sudo apt-get install haproxy
Modify /etc/haproxy/haproxy.cfg to set the maximum number of concurrent connections in the global section:
maxconn <number of concurrent connections>
Also configure temporary DHE key sizes:
tune.ssl.default-dh-param 2048
In defaults (under mode http) add:
option forwardfor
option http-server-close
Specify frontend and backend settings to manage connections:
frontend maas
bind *:443 ssl crt /etc/ssl/private/mysite.com.pem
reqadd X-Forwarded-Proto:\ https
retries 3
option redispatch
default_backend maas
backend maas
timeout server 90s
balance source
hash-type consistent
server localhost localhost:5240 check
server maas-api-1 <ip-address-of-a-region-controller>:5240 check
server maas-api-2 <ip-address-of-another-region-controller>:5240 check
Apply these changes by restarting HAProxy:
sudo systemctl restart haproxy
Enable HAProxy logging if desired. Alternatively, bypass SSL without HAProxy.
Use logging¶
Use MAAS log files to find potential security issues.
See the MAAS logging reference for detailed examples & log file locations.
Manage users¶
Manage users carefully to maintain strong, proactive security.
Add a user¶
UI Settings > Users > Add user> [Fill fields] > Save.
Check the appropriate box to grant administrative rights.
CLI
maas $PROFILE users create username=$USERNAME \
email=$EMAIL_ADDRESS password=$PASSWORD is_superuser=0
Edit users¶
UI [Select user] > Details > [Make changes] > Save
Manage SSH keys¶
UI Settings > Users > [User] > Pencil > [Follow key import steps]
CLI
ubuntu@maas:~$ maas $PROFILE sshkeys create key="$(cat /home/ubuntu/.ssh/id_rsa.pub)"
Manage API Keys¶
Settings > Users > [User] > Pencil > API keys
Change passwords¶
Settings > Users > [User] > Pencil > [Follow instructions]
Note that administrators can change any user’s password. Learn more about strong passwords.
Manage Vault¶
Learn more about MAAS and Hashicorp Vault.
To ensure seamless integration between MAAS and Vault, you’ll first need to obtain a role_id and wrapped_token through Vault’s CLI. For detailed guidance, check Hashicorp Vault’s tutorial.
Here’s an illustrative example on how to set up this integration using the vault CLI:
Enable the
approleengine
vault auth list
If approle/ isn’t mounted, enable it:
vault auth enable approle
Confirm or mount the KV v2 engine
vault secrets enable -path $SECRETS_MOUNT kv-v2
Create a suitable policy
path "$SECRETS_MOUNT/metadata/$SECRETS_PATH/" {
capabilities = ["list"]
}
path "$SECRETS_MOUNT/metadata/$SECRETS_PATH/*" {
capabilities = ["read", "update", "delete", "list"]
}
path "$SECRETS_MOUNT/data/${SECRETS_PATH}/*" {
capabilities = ["read", "create", "update", "delete"]
}
Apply the policy in Vault
vault policy write $MAAS_POLICY $POLICY_FILE
Associate each MAAS region controller with the policy
$ vault write auth/approle/role/$ROLE_NAME \
policies=$MAAS_POLICY token_ttl=5m
Fetch the role ID:
vault read auth/approle/role/$ROLE_NAME/role-id
Generate a secret ID for each role
vault write -wrap-ttl=5m auth/approle/role/$ROLE_NAME/secret-id
Post-setup, you can integrate MAAS with Vault using:
sudo maas config-vault configure $URL $APPROLE_ID $WRAPPED_TOKEN $SECRETS_PATH --mount $SECRET_MOUNT
Complete the integration by migrating the secrets:
sudo maas config-vault migrate
For detailed information, it’s recommended to refer to the Vault documentation and consider Vault certification.