CentOS Stream 10 with GNOME desktop
Overview
This image is CentOS Stream 10 with the GNOME desktop, built from the CentOS Project's own EC2 image with CentOS Stream's Server with GUI environment added. That environment is GNOME on Wayland with the GDM login screen, from the CentOS Stream BaseOS and AppStream repositories. You connect with any RDP client, see the GNOME login screen, and sign in to a full desktop session on the instance.
It is one software version in the SecureAnyCloud Hardened Debian & Linux Desktops AMIs listing on AWS Marketplace. Every desktop in the listing is described at /guides/linux-desktop-aws/.
GNOME's own remote desktop server, gnome-remote-desktop, is set up for remote login. CentOS Stream is a continuously updated distribution that runs slightly ahead of Red Hat Enterprise Linux 10. There are no point releases. The version is always 10, and packages move forward as the CentOS Project publishes them.
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 you get:
- The GNOME desktop from the CentOS Stream 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, and the Red Hat Flatpak remote that CentOS Stream's GNOME group configures ships disabled. The desktop adds nothing an EC2 instance cannot use: no other hypervisors' guest agents and no Xorg server, because the desktop is Wayland only. Printing is not included. The CUPS print server and its filters are removed,
cups-browsedis never installed, and dnf keeps all of them out, so nothing listens on port 631. - A display for GNOME on EC2. GNOME on Wayland needs a kernel display (KMS) device, and an EC2 instance starts with none, because its VGA adapter has no Linux driver bound by default. The image binds Linux's own
bochsdisplay driver to that adapter with one udev rule, so the GNOME login screen also runs on the instance's console. - 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.
sac-remote-desktop enablerefuses 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_l1withoscap --remediateafter the desktop is installed. That way its GNOME rules (login banner, no user list, screen lock, no automount) and its Avahi 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/sac/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 updates left on (
dnf-automatic, installing every update, because CentOS Stream publishes no security advisory data). - 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
CentOS Stream 10 with GNOME Desktop ec2-user
Launch from AWS Marketplace
- Open the listing on AWS Marketplace and choose View purchase options, then Subscribe. One subscription covers every version in the listing.
- Once the subscription is active, choose Launch your software.
- Choose Launch from EC2 Console (or One-click launch). In Version, choose CentOS Stream 10 with GNOME Desktop - YYYY-MM-DD HH:MM, then choose your Region.
- Choose an instance type.
m5.large(2 vCPU, 8 GiB) is recommended, because the GNOME desktop needs the 8 GiB. For smooth everyday use, pickm5.xlargeor larger. There is no GPU requirement, because the desktop renders in software. - Choose your EC2 key pair. It is the only way in.
- Configure the security group to 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).
- Set the 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.
- Leave the metadata settings at their defaults, because the image requires IMDSv2. Then choose Launch.
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 which desktop packages are installed:
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 ptyxisVERSION_ID="10" PRETTY_NAME="CentOS Stream 10 (Coughlan)" graphical.target active gnome-shell 49.10-1.el10 gdm 47.0-32.el10 gnome-remote-desktop 49.3-18.el10 mutter 49.8-1.el10 ptyxis 47.13-3.el10Package versions move forward as CentOS Stream publishes updates.
Check that remote desktop is off
A new instance has remote desktop switched off. The helper shows the whole picture:
sudo sac-remote-desktop status
sudo ss -tlnH | awk '{print $4}' | sortThe expected output looks like this. 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/sac-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 [::]:22The only listener is SSH on port 22.
Set your password
The GNOME login screen asks for your account password, and a new instance has none, so choose your own. Run this command yourself on the instance and enter the new password twice when prompted:
sudo passwd ec2-userThe 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.
Turn remote desktop on
sudo sac-remote-desktop enableExpected output:
remote desktop enabled: listening on port 3389 TLS certificate SHA-256: <your instance's fingerprint> RDP credential: sudo sac-remote-desktop credentials recommended route: ssh -N -L 3389:localhost:3389 <user>@<instance-ip>, then point your RDP client at localhost:3389If 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 sac-remote-desktop credentialsThe 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.
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, computerlocalhost:3389 - macOS: Windows App, add a PC with the name
localhost:3389 - Linux: Remmina (RDP), server
localhost:3389. Or use FreeRDP 3:xfreerdpon CentOS Stream and Fedora,xfreerdp3on Debian and Ubuntu, for examplexfreerdp /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 and press Enter. Then enter the password you set in Step 3, and your desktop session opens. Disconnecting your RDP client leaves the session running, and connecting again returns to it.
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 --reloadUse 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 like your SSH session, and ec2-user keeps its passwordless sudo.
To add software, use dnf in the terminal as on any CentOS Stream 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 getenforcepermitrootlogin no pubkeyauthentication yes passwordauthentication no root L 2025-04-08 0 99999 7 -1 accounts with a password: 0 dhcpv6-client ssh EnforcingAfter 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/sac/ in three forms:
cis-l1-report.html, the HTML reportcis-l1-arf.xml, the machine-readable ARF resultscis-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/sac/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/sac/cis-l1-summary.json'))['failingRules']]"CIS Level 1 Server 0.1.82 - 296 of 298 scored rules pass (99.3%) - waived 7 ensure_journald_and_rsyslog_not_active_together ensure_redhat_gpgkey_installedThe two rules that do not pass. The first is ensure_journald_and_rsyslog_not_active_together. The benchmark wants one logging system, not both, but CentOS Stream 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 standardize on the journal only, run sudo systemctl disable --now rsyslog.
The second is ensure_redhat_gpgkey_installed. The rule looks for Red Hat's release signing key. CentOS Stream packages are signed with the CentOS Project's own key instead, which is installed and which dnf checks on every package (gpgcheck=1 in each repository). Installing Red Hat's key would not change how any CentOS Stream package is verified, so the image does not add it.
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/sac/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/sac/openscap/tailoring.xml \
--profile xccdf_co.sac_profile_cis-l1_ec2_single \
--report ~/cis-l1-rescan.html /usr/share/xml/scap/ssg/content/ssg-cs10-ds.xmlWaived rules
These rules of the profile are deselected through the tailoring file /usr/share/sac/openscap/tailoring.xml and are not scored. The reasons are in /usr/share/doc/sac/cis-l1-waivers.txt. Everything else the profile selects is remediated and scored.
grep -v '^#' /usr/share/doc/sac/cis-l1-waivers.txt | cut -f1,2partition_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 deviceens5: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 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 ...).
Flatpak
CentOS Stream's GNOME group includes redhat-flatpak-repo, which configures a system Flatpak remote named rhel for Red Hat's Flatpak registry. That registry needs a Red Hat account, and the image ships the remote disabled, so no Flatpak remote is enabled:
flatpak remotes --system --show-disabledrhel disabled,oci,no-gpg-verifyTo use it with your Red Hat account, run sudo flatpak remote-modify --enable rhel, or add the remote of your choice with sudo flatpak remote-add.
Printing is not included
The image has no print server. The CUPS server and its filters are removed, cups-browsed is never installed, and /etc/dnf/dnf.conf keeps them out of every dnf transaction, including the automatic updates. The client library cups-libs stays because the desktop libraries link against it. Check it:
rpm -q cups cups-filters cups-browsed libcupsfilters libppd grep '^exclude=' /etc/dnf/dnf.confpackage cups is not installed package cups-filters is not installed package cups-browsed is not installed package libcupsfilters is not installed package libppd is not installed exclude=cups cups-filters cups-browsed libcupsfilters libppdTo add printing yourself, delete those names from the exclude= line, then run sudo dnf install cups and sudo systemctl enable --now cups.
Turn remote desktop off, or replace the RDP credential
To switch remote desktop off again (the setting survives reboots):
sudo sac-remote-desktop disableTo 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 sac-remote-desktop rotate
sudo sac-remote-desktop credentialsPatching
dnf-automatic installs every available update 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.timerdnf check-update exit code: 0 (0 = nothing to update, 100 = updates available) upgrade_type = default apply_updates = yes enabledupgrade_type = default installs every available update, not only security fixes. CentOS Stream's repositories carry no security advisory data, so a security-only setting would install nothing at all. CentOS Stream moves ahead of RHEL and updates arrive continuously, so test updates on a non-production instance first if your workload is sensitive to package changes. To stop automatic updates, run sudo systemctl disable --now dnf-automatic.timer.
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. Automatic updates never restart the instance for you. dnf needs-restarting -r tells you whether a restart is owed.
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 sac-remote-desktop enable first, and check that the SSH tunnel is still running in its own terminal window. sudo sac-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 sac-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
- Check that port 22 is allowed in the security group from your address.
- Use the user
ec2-userand 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, use Session Manager (SSM) as the other way in.
Support
SecureAnyCloud provides 24/7 technical support for this image by email and live chat: sales@secureanycloud.com.
CentOS Stream is free and open source. It is assembled from thousands of independently packaged components, each under its own license (see rpm -qi <package>). No subscription or license key is required.
CentOS is a trademark of Red Hat, Inc. 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. SecureAnyCloud is not affiliated with or endorsed by the CentOS Project, Red Hat, the GNOME Foundation or CIS.
Deploy on AWS
Launch CentOS Stream 10 with GNOME Desktop on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com