How to harden your Landscape deployment

You have many options when hardening your Landscape deployment.

Harden the network

The only application in your Landscape server deployment that should be exposed to incoming external traffic is the reverse proxy, which is either HAProxy or Apache. The reverse proxy listens on these frontend ports:

  • 80 and 443: HTTP and HTTPS traffic, respectively.

  • 6554 (optional): gRPC over HTTP/2 traffic for the Hostagent Messenger service. Expose it only when that service is enabled.

  • 50051 (optional): gRPC over HTTP/2 traffic for the Ubuntu Installer Attach Messenger service. Expose it only when that service is enabled.

For more details on networking, see Internal network requirements.

Landscape’s gRPC services currently support only unencrypted HTTP/2 (h2c). External clients reach the gRPC frontends using HTTP/2 over TLS; TLS terminates at the reverse proxy, which then forwards the traffic to the backend services over h2c. Because these backend connections aren’t TLS, the backend services don’t negotiate ALPN, so their ports must remain internal.

If you’re using Landscape’s repository mirroring features, Landscape Server may need outgoing network access depending on the location of the repositories you’re pulling from.

Port 80 is only needed for Landscape’s repository mirroring features. If you don’t use these features, then you don’t need to expose Port 80. In this case, you would configure your Landscape clients to use HTTPS for all traffic:

  1. Edit /etc/landscape/client.conf to ensure that the entries for url, package_hash_id_url, and ping_url all start with https instead of http

  2. Restart Landscape Client: sudo systemctl restart landscape-client

The other applications in your deployment only require enough network access to communicate with each other. Using the default configuration, these applications use the following ports for incoming traffic:

  • Landscape server: 8070-9100 (inclusive) and 50052

  • PostgreSQL: 5432

  • RabbitMQ server: 5672 for unencrypted TCP or 5671 for TLS-encrypted TCP

Landscape doesn’t listen on every port in the 8070-9100 range; each service uses a base port plus one more port per configured worker, so the exact ports depend on your deployment. Make sure the ports in use are exposed for internal traffic between the applications. None of these ports should be exposed to external traffic.

Secure external traffic

For more security, configure HAProxy or Apache with a TLS certificate. LetsEncrypt provides an easy way to create one: use it with HAProxy by following the Juju HA installation guide, or with Apache by acquiring the certificate the same way and installing it as described in the configure web server section of the manual installation guide. You can also use a self-signed certificate, but you’ll then need to manually distribute it to any Landscape clients you want to register.

Restrict TLS versions and cipher suites

A TLS certificate encrypts traffic, but on its own it still lets the reverse proxy negotiate old protocol versions and weak cipher suites. You can configure your proxy to disable older versions.

Apache

Apache is the reverse proxy for Quickstart and most Manual installations of Landscape. Its virtual host configuration is at /etc/apache2/sites-available/landscape.conf. In each TLS virtual host (each <VirtualHost> block with SSLEngine On), set these directives, replacing the installer’s defaults:

SSLProtocol all -SSLv3 -SSLv2 -TLSv1 -TLSv1.1
SSLHonorCipherOrder On
SSLCompression Off
SSLCipherSuite HIGH:!aNULL:!NULL:!3DES:!DSS:!EXP:!PSK:!SRP:!MD5:!SHA:!CBC:!CAMELLIA

This disables the older SSL and TLS 1.0/1.1 protocols and restricts the TLS 1.2 cipher suites. It doesn’t affect TLS 1.3 ciphers, which Apache configures separately. Then test and reload:

sudo apache2ctl -t
sudo systemctl reload apache2.service

HAProxy

For pre-26.04 Juju deployments, the classic haproxy charm exposes bind options for this:

juju config haproxy global_default_bind_options='no-sslv3 no-tlsv10 no-tlsv11'
juju config haproxy global_default_bind_ciphers='HIGH:!aNULL:!NULL:!3DES:!DSS:!EXP:!PSK:!SRP:!MD5:!SHA:!CBC:!CAMELLIA'

This disables SSL 3.0 and TLS 1.0/1.1 and restricts the TLS 1.2 cipher suites, but doesn’t affect TLS 1.3. Then restart HAProxy on each unit to apply the change:

juju exec --application haproxy "sudo systemctl restart haproxy.service"

Note

Landscape 26.04 LTS and later use a different HAProxy charm, from the 2.8/stable channel, that doesn’t expose these options, so these steps apply only to pre-26.04 deployments. For that charm, see its security documentation for maintained guidance.

Verify the result

From a machine that can reach the reverse proxy, scan it with nmap (sudo apt install nmap) to confirm the offered protocols and ciphers. If your deployment exposes port 6554, include it in the scan as well by using -p 443,6554.

nmap --script ssl-enum-ciphers -p 443 <LANDSCAPE_HOST>

TLS 1.0 and TLS 1.1 should no longer appear.

Secure email traffic

If your deployment relays Landscape’s outgoing email notifications through Postfix, you can require a minimum TLS version for those outgoing connections.

Landscape’s Postfix configuration acts as an SMTP client that relays mail to an external SMTP provider. The Postfix guide already sets smtp_tls_security_level=encrypt, which requires TLS for outgoing mail. To also require a minimum protocol version and restrict the cipher suites, set the mandatory client parameters:

sudo postconf -e 'smtp_tls_mandatory_protocols=>=TLSv1.2'
sudo postconf -e 'smtp_tls_mandatory_ciphers=high'
sudo postconf -e 'smtp_tls_mandatory_exclude_ciphers=aNULL, MD5, SHA, CBC, CAMELLIA'

These smtp_tls_mandatory_* settings apply when TLS is mandatory, that is, when smtp_tls_security_level is encrypt or higher. The >=TLSv1.2 syntax requires Postfix 3.6 or later, which is the version shipped with Ubuntu 22.04 LTS and later releases. These settings restrict the protocols and cipher suites Postfix uses when it connects to your SMTP provider; they do not configure Postfix to accept incoming SMTP connections.

Reload Postfix to apply the change:

sudo postfix reload

Secure the Landscape user

Landscape Server runs all of its services as the service account landscape. landscape’s home directory is /var/lib/landscape.

The landscape user should not be granted write permission to any other directories other than /var/lib/landscape and /tmp.

Harden Landscape Client

Landscape Client runs some of its services as root. This is because some management activities, such as package management, require root privileges.

If you use Landscape’s script execution features, you can restrict what users Landscape can run scripts as by editing the script_users setting in /etc/landscape/client.conf.

If you want to further restrict Landscape Client’s access to the system, configure it to run in “Monitor-only” mode:

  1. Add the line monitor_only = True to /etc/landscape/client.conf

  2. Create or edit the file /etc/default/landscape-client to include DAEMON_USER=landscape

  3. Restart Landscape Client: sudo systemctl restart landscape-client

Keep in mind that management features will be unavailable in Monitor-only mode.

Secure your GPG keys

If you use Landscape’s repository management features, you’ll need to upload a GPG key to Landscape Server. Do not reuse an existing key—you should generate a new key for this purpose.

This GPG private key is used to sign repository package indices. The public key is used by registered clients to validate these signatures. Because the use of the private key is automated, it’s required that the key is not secured with a passphrase.

If for any reason you suspect that the key has been compromised, create a new key, upload it to Landscape, and edit your repository mirrors to use the new key. Landscape will re-sign your repository using the new key. You should then delete the previously-used key.

In Landscape Server 26.04 LTS and later, you don’t upload a single GPG key to Landscape Server. Instead, you provide GPG keys directly on the mirrors and publications that use them:

  • Verification keys are attached to a mirror. For a third-party repository that doesn’t preserve the upstream signing key, provide the ASCII-armored public key in the mirror’s Verification GPG key field. Landscape uses this key to validate the upstream repository’s signature when it syncs the mirror.

  • Signing keys are attached to a publication. To sign (or resign) the release files of a published repository, provide the ASCII-armored GPG private key in the publication’s Signing GPG key field. Registered clients use the corresponding public key to validate these signatures.

Keep the following in mind when managing these keys:

  • Do not reuse an existing key for signing—generate a new key for this purpose.

  • Because signing is automated, the signing private key must not be secured with a passphrase.

  • If you suspect a signing key has been compromised, generate a new key, update the affected publications to use it, and republish. Distribute the new verification (public) key to any registered clients that don’t already have it in their keyring, then delete the compromised key.

Harden Ubuntu

To harden your deployment, you also need to harden the Ubuntu installations that Landscape is deployed on. The best way to ensure your Ubuntu installations are hardened is to make them compliant with security benchmarks.

Ubuntu LTS releases with Ubuntu Pro can take advantage of the Ubuntu Security Guide to ensure they are secure.

Harden Juju

If you used Juju to deploy Landscape, you can follow Juju’s hardening guide to harden the Juju aspects of your deployment.

TLS and mTLS in Landscape

The transport-layer security (TLS) protocol secures communication by requiring the server to present a certificate and private key. With mutual TLS (mTLS), clients must also present a certificate issued by the same certificate authority (CA), so both sides authenticate each other.

Landscape can be configured to use basic TLS or mTLS for its internal services, and for connections to external services like RabbitMQ and HashiCorp Vault.

CA Certificate

The CA certificate is used by servers and clients in Landscape to identify who the certificates were issued by. mTLS insists that both the client and the server present valid TLS credentials issued by the same CA. However, if a TLS server is using self-signed credentials, a client may connect to it by saving the CA certificate in its system CA bundle, or by presenting it when it attempts to connect.

To set up TLS or mTLS, you will need the CA certificate used to sign your certificates. Ensure it has the following permissions:

sudo chmod 644 /path/to/ca/ca-cert.pem
sudo chown root:root /path/to/ca/ca-cert.pem

RabbitMQ

To enable TLS, obtain TLS credentials for the RabbitMQ server and provide their paths in /etc/rabbitmq/rabbitmq.conf, along with other required fields:

listeners.ssl.default = 5671
ssl_options.certfile = /path/to/rabbitmq/server-cert.pem
ssl_options.keyfile = /path/to/rabbitmq/server-key.pem
ssl_options.verify = verify_none
ssl_options.fail_if_no_peer_cert = false

Make sure the TLS credential files are owned by the rabbitmq user:

sudo chown rabbitmq:rabbitmq /path/to/rabbitmq/server-cert.pem /path/to/rabbitmq/server-key.pem
sudo chmod 600 /path/to/rabbitmq/server-key.pem
sudo chmod 644 /path/to/rabbitmq/server-cert.pem

To have the RabbitMQ server enforce mTLS, add the following fields to the config:

ssl_options.cacertfile = /path/to/ca/ca-cert.pem
auth_mechanisms.1 = EXTERNAL
ssl_cert_login_from = common_name

Then, add the following options: ssl_options.verify and ssl_options.fail_if_no_peer_cert:

ssl_options.verify = verify_peer
ssl_options.fail_if_no_peer_cert = true

Edit /etc/rabbitmq/enabled_plugins:

[rabbitmq_auth_mechanism_ssl].

Finally, restart RabbitMQ:

sudo systemctl restart rabbitmq-server

Landscape connects to RabbitMQ via the credentials defined in the [broker] section of your service.conf file, and it can connect via TLS or mTLS.

If RabbitMQ is listening using TLS, add the following field to the [broker] section:

[broker]
ssl_client_ca_cert = /path/to/ca/ca-cert.pem

If RabbitMQ is enforcing mTLS, delete the password field from the section if present and provide the paths to a TLS credential pair to enable certificate-based authentication:

[broker]
ssl_client_cert = /path/to/broker/client-cert.pem
ssl_client_private_key = /path/to/broker/client-key.pem

Ensure the broker credentials are owned by the landscape user:

sudo chown landscape:landscape /path/to/broker/client-cert.pem /path/to/broker/client-key.pem
sudo chmod 600 /path/to/broker/client-key.pem
sudo chmod 644 /path/to/broker/client-cert.pem

Restart Landscape:

sudo lsctl restart

Landscape services

The following Landscape services can be configured to use TLS or mTLS:

  • landscape-async-frontend

  • landscape-secrets-service

Each service can have its own server certificate and can be configured to require clients to authenticate via their own TLS credentials. The secrets-service can additionally be configured to connect to HashiCorp Vault as a client via TLS or mTLS.

Async Frontend

The async-frontend service can listen using TLS or mTLS for incoming connections.

Obtain a TLS server certificate and private key pair, and add the paths in the [async_frontend] section in service.conf:

ssl_server_cert = /path/to/async_frontend/server-cert.pem
ssl_server_private_key = /path/to/async_frontend/server-key.pem

Set ownership and permissions:

sudo chown landscape:landscape /path/to/async_frontend/server-cert.pem /path/to/async_frontend/server-key.pem
sudo chmod 600 /path/to/async_frontend/server-key.pem
sudo chmod 644 /path/to/async_frontend/server-cert.pem

To further enable mTLS, you must also provide the path to the CA cert:

ssl_server_ca_cert = /path/to/ca/ca-cert.pem

Restart Landscape:

sudo lsctl restart

Secrets Service (with HashiCorp Vault)

The secrets-service can listen using TLS or mTLS for incoming connections, and it can connect to a Vault server using TLS or mTLS. See HashiCorp’s guide on hardening your Vault server.

Update the vault_url field in the [secrets] section of your service.conf, and make sure both URLs are using HTTPS:

[secrets]
service_url = https://localhost:26155
vault_url = https://localhost:8200

If the Vault server is listening with TLS, you must also provide the secrets-service with paths to the CA certificate used by Vault:

ssl_client_ca_cert = /path/to/vault/vault-ca.pem

To make the service itself listen using TLS, append the paths to a TLS credential pair:

ssl_server_private_key = /path/to/secrets/server-key.pem
ssl_server_cert        = /path/to/secrets/server-cert.pem

To connect to a Vault server that is enforcing mTLS, obtain or generate client TLS credentials issued by the same CA and append them to the section:

ssl_client_private_key = /path/to/client/client-key.pem
ssl_client_cert = /path/to/client/client-cert.pem

To make the Secrets Service listen using mTLS, you must also include the path to the CA cert:

ssl_server_ca_cert = /path/to/ca/ca-cert.pem

Set ownership and permissions:

sudo chown landscape:landscape /path/to/secrets/server-cert.pem /path/to/secrets/server-key.pem
sudo chmod 600 /path/to/secrets/server-key.pem
sudo chmod 644 /path/to/secrets/server-cert.pem

Restart Landscape:

sudo lsctl restart