Operating Systems AWS

AlmaLinux 10 with GNOME Desktop on AWS User Guide

| Product: AlmaLinux 10 with GNOME Desktop on AWS

Overview

This image is AlmaLinux 10 with the GNOME desktop. It is one software version of the cloudimg Hardened Debian & Linux Desktops AMIs listing on AWS Marketplace. Every desktop in the listing is described at /guides/linux-desktop-aws/.

It is the cloudimg AlmaLinux 10 image (genuine AlmaLinux 10 stable, a numbered 10.N release, never AlmaLinux Kitten) with AlmaLinux's own Server with GUI environment added. That environment is GNOME on Wayland with the GDM login screen, from the AlmaLinux BaseOS and AppStream repositories. GNOME's own remote desktop server, gnome-remote-desktop, is set up for remote login. You connect with any RDP client, see the GNOME login screen, and sign in to a full desktop session on the instance.

Remote desktop ships switched off, and no account has a password. A desktop login always needs a password, so the image never ships one. You set your own over SSH, then turn remote desktop on with one command. The recommended route is an SSH tunnel, so port 3389 never has to be open to the internet.

What cloudimg does to the image:

  • The GNOME desktop from the AlmaLinux repositories only. The image includes GDM, GNOME Shell, Files, the Ptyxis terminal, Firefox, Text Editor, System Monitor and Settings, installed in one transaction with weak dependencies off. There is no third-party repository. Things no EC2 instance can use are left out: GPU and Wi-Fi firmware, other hypervisors' guest agents, the Xorg server (the desktop is Wayland only) and kdump.
  • GNOME Remote Desktop in system mode, off until you enable it. At first boot each instance generates its own RDP credential and its own self-signed TLS certificate, readable by root only. Nothing is shared between two instances and nothing is baked into the image. cloudimg-remote-desktop enable refuses to start until an administrator account has a password, so the login screen is never exposed with nothing behind it.
  • Remediated with the OpenSCAP CIS Level 1 Server profile. The build applies the SCAP Security Guide 0.1.82 profile cis_server_l1 with oscap --remediate after the desktop is installed. That way its GNOME rules (login banner, no user list, screen lock, no automount) and its Avahi, printing and Bluetooth rules apply to this desktop. The build then reboots and runs a scored evaluation, and it fails if fewer than 95% of the scored rules pass. The report ships in the image at /usr/share/doc/cloudimg/cis-l1-report.html. This is a measurement made by open-source tooling. It is not a certification or accreditation by the benchmark's publisher or any other body, and these images are not CIS Hardened Images.
  • Fully patched at build time, with automatic security updates left on (dnf-automatic).
  • Key-only SSH as ec2-user. Password login over SSH is off and stays off after you set a desktop password. Root login is refused and the root account is locked.
  • Platform hardening. The image requires IMDSv2 and runs the firewalld host firewall (inbound SSH only) with SELinux enforcing. It also runs chrony on the Amazon Time Sync Service and the AWS Systems Manager agent.

Connecting to your instance

Connect over SSH as the default login user for the operating system variant you launched:

Operating system variant SSH login user
AlmaLinux 10 with GNOME Desktop ec2-user

Launch from AWS Marketplace

  1. Open the listing on AWS Marketplace and choose View purchase options, then Subscribe. One subscription covers every version in the listing.
  2. Once the subscription is active, choose Launch your software.
  3. Choose Launch from EC2 Console (or One-click launch). In Version, choose AlmaLinux 10 with GNOME Desktop - YYYY-MM-DD HH:MM, then choose your Region.
  4. Instance type: m5.large (2 vCPU, 8 GiB) is recommended, because the GNOME desktop needs the 8 GiB. For smooth everyday use, pick m5.xlarge or larger. There is no GPU requirement: the desktop renders in software.
  5. Key pair: choose your EC2 key pair. It is the only way in.
  6. Security group: allow inbound TCP 22 from your own IP address only. The listing's default security group also offers TCP 3389 for a direct RDP connection. Restrict it to your own address too, or remove it and use the SSH tunnel (Step 5).
  7. Storage: the root volume is 16 GiB gp3 by default. Choose a larger size if you will install applications. cloud-init grows the root filesystem to fill it on first boot.
  8. Leave the metadata settings at their defaults (the image requires IMDSv2), then choose Launch.

Step 1: Connect over SSH and check the desktop

ssh -i ~/.ssh/my-key.pem ec2-user@<public-ip>

ec2-user has passwordless sudo, and no account has a password. Check three things: the release, that the instance booted to the graphical target with the GNOME login screen (GDM) running, and the installed desktop packages:

grep -E '^(PRETTY_NAME|VERSION_ID)=' /etc/os-release
systemctl get-default
systemctl is-active gdm
rpm -q --qf '%{NAME} %{VERSION}-%{RELEASE}\n' gnome-shell gdm gnome-remote-desktop mutter ptyxis
VERSION_ID="10.2"
PRETTY_NAME="AlmaLinux 10.2 (Lavender Lion)"
graphical.target
active
gnome-shell 49.4-9.el10_2.alma.1
gdm 47.0-24.el10_2
gnome-remote-desktop 49.3-4.el10_2
mutter 49.4-4.el10_2
ptyxis 47.13-1.el10

Package versions move forward as AlmaLinux publishes updates.

Step 2: Check that remote desktop is off

A new instance has remote desktop switched off. The helper shows the whole picture:

sudo cloudimg-remote-desktop status
sudo ss -tlnH | awk '{print $4}' | sort

Expected output (the certificate fingerprint is unique to your instance):

remote desktop service: disabled / inactive
RDP backend: disabled
listening on 3389: no
per-instance RDP credential: generated (/root/cloudimg-remote-desktop-credentials.txt)
TLS certificate SHA-256: <your instance's fingerprint>
accounts able to log in at the GNOME login screen: none (set one with: sudo passwd <user>)
0.0.0.0:22
[::]:22

The only listener is SSH on port 22.

Step 3: Set your password

The GNOME login screen asks for your account password, and a new instance has none, so choose your own. Run this yourself on the instance and enter the new password twice when prompted:

sudo passwd ec2-user

The image's password policy is part of the CIS Level 1 remediation. A password needs:

  • at least 14 characters
  • all four character classes: upper case, lower case, a digit and a symbol
  • no more than 3 identical or sequential characters in a row
  • no dictionary words

SSH stays key only. The password works at the GNOME login screen and for nothing else over the network.

Step 4: Turn remote desktop on

sudo cloudimg-remote-desktop enable

Expected output:

remote desktop enabled: listening on port 3389
TLS certificate SHA-256: <your instance's fingerprint>
RDP credential: sudo cloudimg-remote-desktop credentials
recommended route: ssh -N -L 3389:localhost:3389 <user>@<instance-ip>, then point your RDP client at localhost:3389

If no administrator account has a password yet, the helper refuses and tells you which command to run first. The setting survives reboots.

Print your instance's RDP credential, which you need in the next step:

sudo cloudimg-remote-desktop credentials

The output lists these values:

  • RDP_USERNAME (ec2-user)
  • RDP_PASSWORD (24 random characters generated at first boot)
  • RDP_PORT
  • the certificate fingerprints

This credential is for your RDP client only. It is not an operating system password and it does not work for SSH.

Step 5: Connect with your RDP client

The recommended route keeps port 3389 closed and carries RDP inside your SSH connection. The host firewall already admits it, because the tunnel arrives on the loopback interface. On your workstation, open a tunnel and leave it running:

ssh -i ~/.ssh/my-key.pem -N -L 3389:localhost:3389 ec2-user@<public-ip>

Then point your RDP client at localhost:3389:

  • Windows: Remote Desktop Connection (mstsc) or Windows App, computer localhost:3389
  • macOS: Windows App, add a PC with the name localhost:3389
  • Linux: Remmina (RDP), server localhost:3389. Or use FreeRDP 3: xfreerdp on AlmaLinux and Fedora, xfreerdp3 on Debian and Ubuntu, for example xfreerdp /v:localhost:3389 /u:ec2-user /gfx /bpp:32

When the client asks for credentials, enter RDP_USERNAME and RDP_PASSWORD from Step 4. This happens before any screen is drawn. RDP is TLS only with Network Level Authentication, so the client must support NLA (every current client does).

On first connection the client shows the server's certificate. It is self-signed and unique to your instance, so check its fingerprint before you accept it. Compare it with RDP_TLS_SHA256, or with RDP_TLS_SHA1, which is what Windows shows as the thumbprint.

The GNOME login screen then appears with the login banner. It does not list accounts, so type ec2-user, press Enter, then enter the password you set in Step 3. Your desktop session opens. Disconnecting your RDP client leaves the session running, and connecting again returns to it.

The GNOME login screen of the AlmaLinux 10 instance in an RDP session over the SSH tunnel, with the login banner and an empty username field

The AlmaLinux GNOME desktop in the remote session after signing in, with the top bar clock and the screen sharing indicator showing the session is remote

Direct connection instead of the tunnel. Make both of these changes:

  • In the instance's security group, allow TCP 3389 from your own public address only, never from the whole internet.
  • On the instance, admit the same address in the host firewall:
sudo firewall-cmd --permanent --zone=public --add-rich-rule='rule family="ipv4" source address="<your-public-ip>/32" port port="3389" protocol="tcp" accept'
sudo firewall-cmd --reload

Step 6: Use the desktop

Open Activities, or press the Super key, to reach the application grid. The image carries the applications of the Server with GUI group, including Files, the Ptyxis terminal, Firefox, Text Editor, System Monitor and Settings. A terminal inside the session behaves exactly as your SSH session does, and ec2-user keeps its passwordless sudo.

The GNOME application grid in the remote session listing the applications the image ships

The Ptyxis terminal inside the remote GNOME desktop showing AlmaLinux 10.2 and the graphical target

To add software, use dnf in the terminal as on any AlmaLinux machine, for example sudo dnf install -y libreoffice, then open the application from the grid.

Security posture

Confirm four things:

  • SSH is key only.
  • Root is locked.
  • No account has a password until you set one.
  • The host firewall admits SSH only.
sudo sshd -T | grep -E '^(passwordauthentication|permitrootlogin|pubkeyauthentication) '
sudo passwd -S root
sudo awk -F: '$2 !~ /^[!*]/ && $2 != "" {n++} END {print "accounts with a password: " n+0}' /etc/shadow
sudo firewall-cmd --list-services
getenforce
permitrootlogin no
pubkeyauthentication yes
passwordauthentication no
root L 2024-12-14 0 99999 7 -1
accounts with a password: 0
dhcpv6-client ssh
Enforcing

After Step 3, the third line reads 1 (your own account). root L means the root password is locked.

View the compliance report

The evaluation the build ran ships in /usr/share/doc/cloudimg/ in three forms:

  • cis-l1-report.html, the HTML report
  • cis-l1-arf.xml, the machine-readable ARF results
  • cis-l1-summary.json, with the counts, pass rate, threshold, SSG version and checksums

The build host's name and addresses are replaced in the shipped copy.

python3 -c "import json; d=json.load(open('/usr/share/doc/cloudimg/cis-l1-summary.json')); print(d['profileLabel'], d['ssgVersion'], '-', d['pass'], 'of', d['scored'], 'scored rules pass', '(' + str(d['ratePct']) + '%)', '- waived', d['waived'])"
python3 -c "import json; [print(r) for r in json.load(open('/usr/share/doc/cloudimg/cis-l1-summary.json'))['failingRules']]"
CIS Level 1 Server 0.1.82 - 297 of 298 scored rules pass (99.7%) - waived 7
ensure_journald_and_rsyslog_not_active_together

The one rule that does not pass. The benchmark wants one logging system, not both. AlmaLinux runs journald and rsyslog together by default. journald keeps the structured system journal, and rsyslog writes the traditional text logs (/var/log/messages, /var/log/secure) that many log shippers and audit procedures read. The image keeps both so neither kind of tooling loses its logs. If you standardise on the journal only, run sudo systemctl disable --now rsyslog.

Copy the HTML report to your workstation and open it in a browser:

scp -i ~/.ssh/my-key.pem ec2-user@<public-ip>:/usr/share/doc/cloudimg/cis-l1-report.html .

You can re-run the same evaluation on your instance at any time. It takes about 5 to 10 minutes and changes nothing. oscap exits with status 2 when any rule fails, and the report is still written:

sudo oscap xccdf eval --tailoring-file /usr/share/cloudimg/openscap/tailoring.xml \
  --profile xccdf_co.cloudimg_profile_cis-l1_ec2_single \
  --report ~/cis-l1-rescan.html /usr/share/xml/scap/ssg/content/ssg-almalinux10-ds.xml

Waived rules

These rules of the profile are deselected through the tailoring file /usr/share/cloudimg/openscap/tailoring.xml and are not scored. The reasons are in /usr/share/doc/cloudimg/cis-l1-waivers.txt. Everything else the profile selects is remediated and scored.

grep -v '^#' /usr/share/doc/cloudimg/cis-l1-waivers.txt | cut -f1,2
partition_for_tmp   [single-volume-partitions]
grub2_password  [serial-console-and-boot]
ensure_root_password_configured [serial-console-and-boot]
accounts_maximum_age_login_defs [key-only-account-aging]
account_disable_post_pw_expiration  [key-only-account-aging]
sshd_limit_user_access  [ssh-access-list]
service_systemd-journal-upload_enabled  [log-remote-destination]
Rule Why it is waived on EC2
partition_for_tmp The image boots from one EBS root volume, so /tmp, /var, /var/log, /var/log/audit, /var/tmp and /home live on the root filesystem.
grub2_password, ensure_root_password_configured A boot-loader or root password would have to be baked into the image, giving every instance the same credential. Root stays locked and no password exists.
accounts_maximum_age_login_defs, account_disable_post_pw_expiration Password-age defaults apply to every account cloud-init creates at launch. Those accounts are key only and have no password, so an age limit would expire them and lock the SSH key out. Accounts that do have a password (yours, after Step 3) are still aged.
sshd_limit_user_access An AllowUsers/AllowGroups list fixed at build time would refuse SSH to accounts you create with cloud-init user data. SSH is key only, root login is refused, and your security group restricts the source addresses.
service_systemd-journal-upload_enabled The profile turns on journal upload to a remote log server but sets no destination, and an image cannot know your server. To use it, put URL= for your collector in /etc/systemd/journal-upload.conf, then run sudo systemctl enable --now systemd-journal-upload.

Networking and services that ship switched off

NetworkManager manages the EC2 network interface, and cloud-init writes its connection profile at first boot:

nmcli -t -f DEVICE,TYPE,STATE device
eth0:ethernet:connected
lo:loopback:connected (externally)

The Server with GUI group brings several network services that this image leaves switched off:

  • cockpit.socket (the Cockpit web console) is disabled.
  • Avahi, CUPS printing and Bluetooth are masked by the CIS Level 1 remediation.
  • ModemManager is disabled.

To use the Cockpit web console, run sudo systemctl enable --now cockpit.socket. Then reach it on port 9090 through an SSH tunnel (ssh -N -L 9090:localhost:9090 ...).

Turn remote desktop off, or replace the RDP credential

To switch remote desktop off again (the setting survives reboots):

sudo cloudimg-remote-desktop disable

To replace the generated RDP password with a new random one, run rotate, then print the new credential. The new password reaches the remote desktop server on standard input, so it never appears in a log or a process list.

sudo cloudimg-remote-desktop rotate
sudo cloudimg-remote-desktop credentials

Patching

dnf-automatic installs security updates automatically each day. Check what is pending now:

sudo dnf -q check-update --refresh >/dev/null; echo "dnf check-update exit code: $? (0 = nothing to update, 100 = updates available)"
grep -E '^(upgrade_type|apply_updates)' /etc/dnf/automatic.conf
systemctl is-enabled dnf-automatic.timer
dnf check-update exit code: 0 (0 = nothing to update, 100 = updates available)
upgrade_type = security
apply_updates = yes
enabled

An instance launched later lists the updates released since, with exit code 100. To install everything now, run sudo dnf -y upgrade, then sudo systemctl reboot if a new kernel was installed.

Troubleshooting

The RDP client connects, then the screen stays black or it disconnects

GNOME Remote Desktop requires the RDP graphics pipeline. Current Windows and macOS clients and Remmina support it. With FreeRDP, pass /gfx /bpp:32; without those flags the server closes the connection after authentication. Read the server side with:

sudo journalctl -u gnome-remote-desktop --no-pager -n 20

"Connection refused" on localhost:3389

Remote desktop is switched off on a new instance by design. Run sudo cloudimg-remote-desktop enable first, and check that the SSH tunnel is still running in its own terminal window. sudo cloudimg-remote-desktop status shows whether anything is listening on 3389.

The RDP client says the credentials are wrong

The client wants the RDP credential from sudo cloudimg-remote-desktop credentials, not your account password. The account password is only for the GNOME login screen that appears afterwards.

The login screen does not accept my password

The login screen does not list accounts, so type ec2-user (or your account name), then your password. If the account has no password yet, set it with sudo passwd ec2-user over SSH.

After 5 failed attempts the account is locked for 15 minutes (part of the CIS Level 1 remediation). To unlock it early, run sudo faillock --user ec2-user --reset.

The session locked while I was away

The screen locks after 15 minutes of inactivity (CIS Level 1). Unlock it with your account password.

Cannot connect via SSH

  1. Check that port 22 is allowed in the security group from your address.
  2. Use the user ec2-user and the key pair you chose at launch. Password login over SSH is disabled by design.

A service is blocked and the logs mention SELinux

SELinux is enforcing. Read the denial with sudo ausearch -m avc -ts recent before you change anything. Fix the label or the boolean it names rather than turning SELinux off.

Emergency access

No account has a password until you set one. EC2 Serial Console and single-user recovery need a local password, and the one you set in Step 3 works for the serial console. If SSH is unavailable, Session Manager (SSM) is the other way in.

Support

cloudimg provides 24/7 technical support for this image by email and live chat: support@cloudimg.co.uk.

AlmaLinux is free and open source. It is assembled from thousands of independently packaged components, each under its own licence (see rpm -qi <package>). No subscription or licence key is required.

AlmaLinux is a trademark of the AlmaLinux OS Foundation. GNOME is a trademark of the GNOME Foundation. CIS and CIS Benchmarks are trademarks of the Center for Internet Security, Inc. The names are used only to identify the software this image contains. cloudimg is not affiliated with or endorsed by the AlmaLinux OS Foundation, the GNOME Foundation or CIS.