Migrate XRDP and Cloudflare playbooks

This commit is contained in:
Haitao Pan 2026-04-05 16:54:48 +08:00
parent d195a21a66
commit 47504726a3
39 changed files with 865 additions and 45 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
.env
.artifacts/acp_codex/xworkmate-go-core .artifacts/acp_codex/xworkmate-go-core
.artifacts/acp_opencode/xworkmate-go-core .artifacts/acp_opencode/xworkmate-go-core

View File

@ -1,6 +1,7 @@
[defaults] [defaults]
# 常用参数 # 常用参数
inventory = ./inventory # 默认清单文件路径,可按需改 # 默认清单文件路径,可按需改
inventory = ./inventory.ini
vault_password_file = ~/.vault_password vault_password_file = ~/.vault_password
timeout = 10 timeout = 10
forks = 10 forks = 10

7
gnome_xrdp_minimal.yaml Normal file
View File

@ -0,0 +1,7 @@
---
- name: Setup minimal GNOME + XRDP desktop
hosts: all
become: true
gather_facts: true
roles:
- roles/vhosts/gnome_xrdp_minimal/

View File

@ -0,0 +1,17 @@
---
- name: Harden SSH on all inventory hosts
hosts: all
become: true
gather_facts: true
vars:
sshd_config_path: /etc/ssh/sshd_config
sshd_dropin_dir: /etc/ssh/sshd_config.d
root_authorized_keys_path: /root/.ssh/authorized_keys
local_public_key_path: "{{ lookup('env', 'HOME') }}/.ssh/id_rsa.pub"
ansible_user: "{{ lookup('env', 'BOOTSTRAP_ROOT_USER') | default('root', true) }}"
ansible_password: "{{ lookup('env', 'BOOTSTRAP_ROOT_PASSWORD') | default(omit, true) }}"
ansible_become_password: "{{ lookup('env', 'BOOTSTRAP_BECOME_PASSWORD') | default(omit, true) }}"
roles:
- role: harden_ssh_root_key_only

View File

@ -1,39 +1,19 @@
[web] [web]
cn-website.svc.plus ansible_host=47.120.61.35 cn-front.svc.plus ansible_host=47.120.61.35 ansible_user=root
global-console.svc.plus ansible_host=35.220.157.80 ansible_user=root
hk-website ansible_host=35.220.224.163 ansible_user=root
[deepflow_agents] [agent_proxy]
192.168.1.101 ansible_user=root ansible_ssh_pass=pass101 jp-xhttp-contabo.svc.plus ansible_host=46.250.251.132 ansible_user=root
192.168.1.102 ansible_user=admin ansible_ssh_pass=pass102
192.168.1.103 ansible_user=root ansible_ssh_pass=pass103 ansible_port=2222
192.168.1.104 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa_ubuntu
[mail]
smtp.svc.plus ansible_host=45.130.167.90
[k3s] [k3s]
cn-k3s-vultr.svc.plus ansible_host=1.15.155.245 ansible_user=root ansible_ssh_private_key_file=~/.ssh/id_rsa jp-k3s-vultr.svc.plus ansible_host=167.179.110.129 ansible_user=root
jp-xhttp-contabo.svc.plus ansible_host=46.250.251.132 ansible_user=root ansible_ssh_private_key_file=~/.ssh/id_rsa
jp-k3s-vultr.svc.plus ansible_host=jp-k3s-vultr.svc.plus ansible_user=root
k3s_platform_git_private_key_path=~/.ssh/id_rsa
[bootstrap]
auth.svc.plus ansible_host=34.92.122.119 ansible_user=root ansible_ssh_private_key_file=~/.ssh/id_rsa
[all:vars] [all:vars]
ansible_port=22 ansible_port=22
ansible_user=root ansible_user=root
ansible_ssh_transfer_method=piped
ansible_host_key_checking=False ansible_host_key_checking=False
# SSH 密钥或密码(二选一) # SSH 密钥或密码(二选一)
# ansible_ssh_private_key_file=~/.ssh/id_rsa ansible_ssh_private_key_file=~/.ssh/id_rsa
# ansible_ssh_pass=your_password k3s_platform_git_private_key=~/.ssh/id_rsa
# DeepFlow agent 配置变量
controller_ips=["10.10.10.10", "10.10.10.11"]
vtap_group_id="g-P22vLIMdB6"
# DeepFlow agent 安装包位置
agent_base_dir="deepflow-agent-for-linux"
agent_package_name="deepflow-agent-1.0-5407.systemd.x86_64.rpm"

7
plasma_xrdp_minimal.yaml Normal file
View File

@ -0,0 +1,7 @@
---
- name: Setup minimal Plasma + XRDP desktop
hosts: all
become: true
gather_facts: true
roles:
- roles/vhosts/plasma_xrdp_minimal/

View File

@ -61,12 +61,16 @@
- name: Set Cloudflare zone id - name: Set Cloudflare zone id
ansible.builtin.set_fact: ansible.builtin.set_fact:
cloudflare_dns_zone_id: "{{ cloudflare_dns_zone_lookup.json.result[0].id }}" cloudflare_dns_zone_id: "{{ cloudflare_dns_zone_lookup.json.result[0].id }}"
when: cloudflare_dns_zone_lookup is defined when:
- cloudflare_dns_zone_lookup is defined
- cloudflare_dns_zone_id | default('', true) | length == 0
- name: Show zone permissions for current token - name: Show zone permissions for current token
ansible.builtin.debug: ansible.builtin.debug:
msg: "Cloudflare token permissions for {{ cloudflare_dns_zone_name }}: {{ cloudflare_dns_zone_lookup.json.result[0].permissions | default([]) }}" msg: "Cloudflare token permissions for {{ cloudflare_dns_zone_name }}: {{ cloudflare_dns_zone_lookup.json.result[0].permissions | default([]) }}"
when: cloudflare_dns_zone_lookup is defined when:
- cloudflare_dns_zone_lookup is defined
- cloudflare_dns_zone_lookup.json is defined
- name: Fail early when token does not have DNS edit permission - name: Fail early when token does not have DNS edit permission
ansible.builtin.assert: ansible.builtin.assert:
@ -77,7 +81,9 @@
CLOUDFLARE_API_TOKEN is valid but lacks DNS edit permission for {{ cloudflare_dns_zone_name }}. CLOUDFLARE_API_TOKEN is valid but lacks DNS edit permission for {{ cloudflare_dns_zone_name }}.
Current permissions: {{ cloudflare_dns_zone_lookup.json.result[0].permissions | default([]) }}. Current permissions: {{ cloudflare_dns_zone_lookup.json.result[0].permissions | default([]) }}.
Required: Zone read + DNS edit on the svc.plus zone. Required: Zone read + DNS edit on the svc.plus zone.
when: cloudflare_dns_zone_lookup is defined when:
- cloudflare_dns_zone_lookup is defined
- cloudflare_dns_zone_lookup.json is defined
- name: Query existing DNS records - name: Query existing DNS records
ansible.builtin.uri: ansible.builtin.uri:

View File

@ -0,0 +1,12 @@
# cloudflare_svc_plus_dns
Specialized wrapper role for the `svc.plus` DNS set.
## What it does
- Loads the managed record manifest from `playbooks/vars/cloudflare_svc_plus_dns.yml`
- Delegates the actual create/update/delete reconciliation to the shared `cloudflare_dns` role
## Entry point
Use `update_cloudflare_svc_plus_dns.yml` to apply this role.

View File

@ -0,0 +1,8 @@
---
- name: Load svc.plus DNS manifest
ansible.builtin.include_vars:
file: "{{ role_path }}/../../vars/cloudflare_svc_plus_dns.yml"
- name: Reconcile svc.plus DNS via shared Cloudflare role
ansible.builtin.include_role:
name: cloudflare_dns

View File

@ -0,0 +1,43 @@
# harden_ssh_root_key_only
Harden SSH access for inventory hosts by installing the local operator public key for `root` and disabling password-based SSH authentication.
## What it does
- Reads `~/.ssh/id_rsa.pub` from the local machine by default
- Installs that public key into `/root/.ssh/authorized_keys`
- Writes an SSH daemon drop-in at `/etc/ssh/sshd_config.d/99-codex-root-key-only.conf`
- Disables password and keyboard-interactive SSH auth
- Reloads the SSH service after validating config syntax
## Variables
- `local_public_key_path`: path to the operator public key, default `~/.ssh/id_rsa.pub`
- `root_authorized_keys_path`: root authorized_keys path, default `/root/.ssh/authorized_keys`
- `sshd_dropin_dir`: SSH drop-in directory, default `/etc/ssh/sshd_config.d`
- `ssh_service_name_override`: optional service name override, otherwise auto-detects `ssh` or `sshd`
## Example playbook
```yaml
- hosts: all
become: true
roles:
- role: harden_ssh_root_key_only
```
## Validation
After running the role, verify:
```bash
sshd -T | egrep '^(passwordauthentication|kbdinteractiveauthentication|pubkeyauthentication|permitrootlogin)'
passwd -S root
```
Expected:
- `PasswordAuthentication no`
- `KbdInteractiveAuthentication no`
- `PubkeyAuthentication yes`
- `root` remains accessible with the installed key

View File

@ -0,0 +1,6 @@
---
sshd_config_path: /etc/ssh/sshd_config
sshd_dropin_dir: /etc/ssh/sshd_config.d
root_authorized_keys_path: /root/.ssh/authorized_keys
local_public_key_path: "{{ lookup('env', 'HOME') }}/.ssh/id_rsa.pub"
ssh_service_name_override: ""

View File

@ -0,0 +1,82 @@
---
- name: Read local SSH public key
ansible.builtin.set_fact:
local_ssh_public_key: "{{ lookup('ansible.builtin.file', local_public_key_path) | trim }}"
- name: Assert local SSH public key exists
ansible.builtin.assert:
that:
- local_ssh_public_key | length > 0
fail_msg: "local_public_key_path must point to a readable SSH public key."
- name: Ensure root SSH directory exists
ansible.builtin.file:
path: /root/.ssh
state: directory
mode: "0700"
owner: root
group: root
- name: Install local public key for root
ansible.posix.authorized_key:
user: root
key: "{{ local_ssh_public_key }}"
state: present
manage_dir: false
- name: Ensure sshd drop-in directory exists
ansible.builtin.file:
path: "{{ sshd_dropin_dir }}"
state: directory
mode: "0755"
owner: root
group: root
- name: Write sshd hardening drop-in
ansible.builtin.copy:
dest: "{{ sshd_dropin_dir }}/99-codex-root-key-only.conf"
mode: "0644"
owner: root
group: root
content: |
PermitRootLogin yes
PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
ChallengeResponseAuthentication no
UsePAM yes
- name: Validate sshd configuration syntax
ansible.builtin.command: sshd -t
changed_when: false
when: not ansible_check_mode
- name: Collect service facts
ansible.builtin.service_facts:
changed_when: false
- name: Select SSH service name
ansible.builtin.set_fact:
ssh_service_name: >-
{{
ssh_service_name_override
if ssh_service_name_override | length > 0
else ('ssh' if 'ssh.service' in ansible_facts.services else 'sshd')
}}
- name: Reload SSH service
ansible.builtin.service:
name: "{{ ssh_service_name }}"
state: reloaded
- name: Read root authorized_keys
ansible.builtin.slurp:
src: "{{ root_authorized_keys_path }}"
register: root_authorized_keys
changed_when: false
- name: Assert public key was installed
ansible.builtin.assert:
that:
- local_ssh_public_key in (root_authorized_keys.content | b64decode)
fail_msg: "Local public key was not installed into /root/.ssh/authorized_keys"

View File

@ -27,6 +27,18 @@
mode: "0755" mode: "0755"
notify: Restart opencode acp bridge notify: Restart opencode acp bridge
- name: Ensure OpenCode runtime directories exist
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ acp_opencode_service_user }}"
group: "{{ acp_opencode_service_group }}"
mode: "0755"
loop:
- "{{ acp_opencode_home }}"
- "{{ acp_opencode_home }}/.local"
- "{{ acp_opencode_workdir }}"
- name: Deploy Caddy main file - name: Deploy Caddy main file
ansible.builtin.template: ansible.builtin.template:
src: Caddyfile.j2 src: Caddyfile.j2

View File

@ -0,0 +1,33 @@
# gnome_xrdp_minimal
Minimal GNOME + XRDP role for Debian, Ubuntu, Fedora, and OpenSuse VPS hosts.
## What it does
- Installs a minimal GNOME desktop stack and XRDP
- Installs the Xorg backend needed by XRDP (`xserver-xorg-core`, `xorgxrdp`)
- Installs open-source Chromium browser
- Installs Chinese font support (`fonts-noto-cjk`)
- Creates `~/.xsession` for the target desktop user
- Enables `xrdp` and `xrdp-sesman`
- Optionally opens TCP `3389` with UFW if UFW is present
- Creates or updates the desktop user and ensures it has a usable local password for XRDP login
## Variables
- `gnome_user`: desktop login user, default `ubuntu`
- `gnome_packages`: minimal package list
- `gnome_enable_ufw`: whether to allow the RDP port with UFW
- `gnome_rdp_port`: RDP port, default `3389`
- `gnome_user_groups`: supplemental groups for the desktop user, default `["sudo"]`
- `gnome_user_password_plaintext`: required password for the desktop user so XRDP can authenticate
- Supported platforms: Debian/Ubuntu, Fedora, OpenSuse
## Example playbook
```yaml
- hosts: vps
become: true
roles:
- role: roles/vhosts/gnome_xrdp_minimal
```

View File

@ -0,0 +1,48 @@
---
gnome_user: ubuntu
gnome_packages_debian:
- gnome-session
- gnome-shell
- gnome-terminal
- nautilus
- dbus-x11
- x11-xserver-utils
- xserver-xorg-core
- xorgxrdp
- chromium
- fonts-noto-cjk
- xrdp
gnome_packages_fedora:
- gnome-session
- gnome-shell
- gnome-terminal
- nautilus
- dbus-x11
- xorg-x11-server-Xorg
- xorgxrdp
- chromium
- google-noto-sans-cjk-fonts
- xrdp
gnome_packages_opensuse:
- gnome-session
- gnome-shell
- gnome-terminal
- nautilus
- dbus-1-x11
- xorg-x11-server
- xorgxrdp
- chromium
- noto-sans-cjk-fonts
- xrdp
gnome_enable_ufw: true
gnome_rdp_port: 3389
gnome_manage_user: true
gnome_user_shell: /bin/bash
gnome_user_groups:
- sudo
gnome_user_password_plaintext: ""

View File

@ -0,0 +1,10 @@
---
- name: Restart xrdp
ansible.builtin.service:
name: xrdp
state: restarted
- name: Restart xrdp sesman
ansible.builtin.service:
name: xrdp-sesman
state: restarted

View File

@ -0,0 +1,53 @@
---
- name: Ensure the desktop user exists
ansible.builtin.user:
name: "{{ gnome_user }}"
shell: "{{ gnome_user_shell }}"
create_home: true
state: present
password_lock: false
become: true
when: gnome_manage_user | bool
- name: Fail when the desktop user password is not provided
ansible.builtin.assert:
that:
- gnome_user_password_plaintext | length > 0
fail_msg: >-
gnome_user_password_plaintext must be set so XRDP can authenticate the
desktop user.
when: gnome_manage_user | bool
- name: Set desktop user password for XRDP login
ansible.builtin.user:
name: "{{ gnome_user }}"
password: "{{ gnome_user_password_plaintext | password_hash('sha512') }}"
update_password: always
password_lock: false
become: true
no_log: true
when: gnome_manage_user | bool
- name: Ensure the desktop user can sudo
ansible.builtin.user:
name: "{{ gnome_user }}"
groups: "{{ gnome_user_groups }}"
append: true
state: present
become: true
when:
- gnome_manage_user | bool
- gnome_user_groups | length > 0
- name: Ensure GNOME session file is present
ansible.builtin.template:
src: xsession.j2
dest: "{{ gnome_xsession_file }}"
owner: "{{ gnome_user }}"
group: "{{ gnome_user }}"
mode: "0644"
become: true
when: gnome_manage_user | bool
notify:
- Restart xrdp
- Restart xrdp sesman

View File

@ -0,0 +1,99 @@
---
- name: Select GNOME package list for this platform
ansible.builtin.set_fact:
gnome_selected_packages: >-
{{
gnome_packages_debian if ansible_os_family == 'Debian'
else gnome_packages_fedora if ansible_os_family == 'RedHat'
else gnome_packages_opensuse if ansible_os_family == 'Suse'
else []
}}
- name: Fail on unsupported platform
ansible.builtin.assert:
that:
- gnome_selected_packages | length > 0
fail_msg: "gnome_xrdp_minimal supports Debian, Ubuntu, Fedora, and OpenSuse only."
- name: Update apt cache
ansible.builtin.apt:
update_cache: true
when: ansible_os_family == 'Debian'
become: true
- name: Install minimal desktop packages
ansible.builtin.apt:
name: "{{ gnome_selected_packages }}"
state: present
install_recommends: false
environment:
DEBIAN_FRONTEND: noninteractive
APT_LISTCHANGES_FRONTEND: none
when: ansible_os_family == 'Debian'
become: true
- name: Update DNF cache
ansible.builtin.dnf:
update_cache: true
when: ansible_os_family == 'RedHat'
become: true
- name: Install GNOME packages on Fedora
ansible.builtin.dnf:
name: "{{ gnome_selected_packages }}"
state: present
when: ansible_os_family == 'RedHat'
become: true
- name: Refresh Zypper cache
ansible.builtin.command: zypper --non-interactive refresh
changed_when: false
when: ansible_os_family == 'Suse'
become: true
- name: Install GNOME packages on OpenSuse
ansible.builtin.zypper:
name: "{{ gnome_selected_packages }}"
state: present
type: package
when: ansible_os_family == 'Suse'
become: true
- name: Check whether the xrdp service account exists
ansible.builtin.command: getent passwd xrdp
register: gnome_xrdp_account
changed_when: false
failed_when: false
become: true
- name: Ensure xrdp user can read the TLS certificate group
ansible.builtin.user:
name: xrdp
groups: ssl-cert
append: true
become: true
when: gnome_xrdp_account.rc == 0
- name: Enable and start XRDP services
ansible.builtin.service:
name: "{{ item }}"
enabled: true
state: started
loop: "{{ gnome_xrdp_services }}"
become: true
- name: Check whether UFW is installed
ansible.builtin.stat:
path: /usr/sbin/ufw
register: gnome_ufw_binary
become: true
- name: Allow XRDP through UFW
ansible.builtin.command: "ufw allow {{ gnome_rdp_port }}/tcp"
register: gnome_ufw_allow
changed_when: "'Skipping adding existing rule' not in gnome_ufw_allow.stdout"
failed_when: false
become: true
when:
- gnome_enable_ufw | bool
- gnome_ufw_binary.stat.exists | default(false)

View File

@ -0,0 +1,16 @@
---
- name: Install minimal GNOME + XRDP stack
ansible.builtin.import_tasks: install.yml
tags: [gnome, gnome_install]
- name: Configure GNOME session and XRDP user setup
ansible.builtin.import_tasks: config.yml
tags: [gnome, gnome_config]
- name: Apply GNOME resource-saving tweaks
ansible.builtin.import_tasks: optimize.yml
tags: [gnome, gnome_optimize]
- name: Validate XRDP desktop readiness
ansible.builtin.import_tasks: validate.yml
tags: [gnome, gnome_validate]

View File

@ -0,0 +1,10 @@
---
- name: Ensure GNOME session settings are persisted
ansible.builtin.file:
path: "{{ gnome_user_home }}/.config"
state: directory
owner: "{{ gnome_user }}"
group: "{{ gnome_user }}"
mode: "0755"
become: true
when: gnome_manage_user | bool

View File

@ -0,0 +1,33 @@
---
- name: Check XRDP service status
ansible.builtin.command: systemctl status xrdp --no-pager --full
register: gnome_xrdp_status
changed_when: false
failed_when: false
become: true
- name: Show XRDP service status
ansible.builtin.debug:
var: gnome_xrdp_status.stdout_lines
- name: Check memory usage
ansible.builtin.command: free -m
register: gnome_memory_usage
changed_when: false
failed_when: false
- name: Show memory usage
ansible.builtin.debug:
var: gnome_memory_usage.stdout_lines
- name: Check current session type if available
ansible.builtin.shell: 'printf "%s\n" "${XDG_SESSION_TYPE:-unknown}"'
args:
executable: /bin/bash
register: gnome_session_type
changed_when: false
failed_when: false
- name: Show session type hint
ansible.builtin.debug:
msg: "XDG_SESSION_TYPE={{ gnome_session_type.stdout | default('unknown') }} (expected x11 inside the RDP session)"

View File

@ -0,0 +1 @@
exec gnome-session

View File

@ -0,0 +1,6 @@
---
gnome_user_home: "/home/{{ gnome_user }}"
gnome_xsession_file: "{{ gnome_user_home }}/.xsession"
gnome_xrdp_services:
- xrdp-sesman
- xrdp

View File

@ -0,0 +1,33 @@
# plasma_xrdp_minimal
Minimal Plasma + XRDP role for Debian, Ubuntu, Fedora, and OpenSuse VPS hosts.
## What it does
- Installs a minimal Plasma desktop stack and XRDP
- Installs the Xorg backend needed by XRDP (`xserver-xorg-core`, `xorgxrdp`)
- Installs open-source Chromium browser
- Installs Chinese font support (`fonts-noto-cjk`)
- Creates `~/.xsession` for the target desktop user
- Enables `xrdp` and `xrdp-sesman`
- Optionally opens TCP `3389` with UFW if UFW is present
- Creates or updates the desktop user and ensures it has a usable local password for XRDP login
## Variables
- `plasma_user`: desktop login user, default `ubuntu`
- `plasma_packages`: minimal package list
- `plasma_enable_ufw`: whether to allow the RDP port with UFW
- `plasma_rdp_port`: RDP port, default `3389`
- `plasma_user_groups`: supplemental groups for the desktop user, default `["sudo"]`
- `plasma_user_password_plaintext`: required password for the desktop user so XRDP can authenticate
- Supported platforms: Debian/Ubuntu, Fedora, OpenSuse
## Example playbook
```yaml
- hosts: vps
become: true
roles:
- role: roles/vhosts/plasma_xrdp_minimal
```

View File

@ -0,0 +1,48 @@
---
plasma_user: ubuntu
plasma_packages_debian:
- plasma-desktop
- kde-plasma-desktop
- konsole
- dolphin
- dbus-x11
- x11-xserver-utils
- xserver-xorg-core
- xorgxrdp
- chromium
- fonts-noto-cjk
- xrdp
plasma_packages_fedora:
- plasma-desktop
- plasma-workspace
- konsole
- dolphin
- dbus-x11
- xorg-x11-server-Xorg
- xorgxrdp
- chromium
- google-noto-sans-cjk-fonts
- xrdp
plasma_packages_opensuse:
- plasma-desktop
- plasma5-session
- konsole
- dolphin
- dbus-1-x11
- xorg-x11-server
- xorgxrdp
- chromium
- noto-sans-cjk-fonts
- xrdp
plasma_enable_ufw: true
plasma_rdp_port: 3389
plasma_manage_user: true
plasma_user_shell: /bin/bash
plasma_user_groups:
- sudo
plasma_user_password_plaintext: ""

View File

@ -0,0 +1,10 @@
---
- name: Restart xrdp
ansible.builtin.service:
name: xrdp
state: restarted
- name: Restart xrdp sesman
ansible.builtin.service:
name: xrdp-sesman
state: restarted

View File

@ -0,0 +1,53 @@
---
- name: Ensure the desktop user exists
ansible.builtin.user:
name: "{{ plasma_user }}"
shell: "{{ plasma_user_shell }}"
create_home: true
state: present
password_lock: false
become: true
when: plasma_manage_user | bool
- name: Fail when the desktop user password is not provided
ansible.builtin.assert:
that:
- plasma_user_password_plaintext | length > 0
fail_msg: >-
plasma_user_password_plaintext must be set so XRDP can authenticate the
desktop user.
when: plasma_manage_user | bool
- name: Set desktop user password for XRDP login
ansible.builtin.user:
name: "{{ plasma_user }}"
password: "{{ plasma_user_password_plaintext | password_hash('sha512') }}"
update_password: always
password_lock: false
become: true
no_log: true
when: plasma_manage_user | bool
- name: Ensure the desktop user can sudo
ansible.builtin.user:
name: "{{ plasma_user }}"
groups: "{{ plasma_user_groups }}"
append: true
state: present
become: true
when:
- plasma_manage_user | bool
- plasma_user_groups | length > 0
- name: Ensure Plasma session file is present
ansible.builtin.template:
src: xsession.j2
dest: "{{ plasma_xsession_file }}"
owner: "{{ plasma_user }}"
group: "{{ plasma_user }}"
mode: "0644"
become: true
when: plasma_manage_user | bool
notify:
- Restart xrdp
- Restart xrdp sesman

View File

@ -0,0 +1,99 @@
---
- name: Select Plasma package list for this platform
ansible.builtin.set_fact:
plasma_selected_packages: >-
{{
plasma_packages_debian if ansible_os_family == 'Debian'
else plasma_packages_fedora if ansible_os_family == 'RedHat'
else plasma_packages_opensuse if ansible_os_family == 'Suse'
else []
}}
- name: Fail on unsupported platform
ansible.builtin.assert:
that:
- plasma_selected_packages | length > 0
fail_msg: "plasma_xrdp_minimal supports Debian, Ubuntu, Fedora, and OpenSuse only."
- name: Update apt cache
ansible.builtin.apt:
update_cache: true
when: ansible_os_family == 'Debian'
become: true
- name: Install minimal desktop packages
ansible.builtin.apt:
name: "{{ plasma_selected_packages }}"
state: present
install_recommends: false
environment:
DEBIAN_FRONTEND: noninteractive
APT_LISTCHANGES_FRONTEND: none
when: ansible_os_family == 'Debian'
become: true
- name: Update DNF cache
ansible.builtin.dnf:
update_cache: true
when: ansible_os_family == 'RedHat'
become: true
- name: Install Plasma packages on Fedora
ansible.builtin.dnf:
name: "{{ plasma_selected_packages }}"
state: present
when: ansible_os_family == 'RedHat'
become: true
- name: Refresh Zypper cache
ansible.builtin.command: zypper --non-interactive refresh
changed_when: false
when: ansible_os_family == 'Suse'
become: true
- name: Install Plasma packages on OpenSuse
ansible.builtin.zypper:
name: "{{ plasma_selected_packages }}"
state: present
type: package
when: ansible_os_family == 'Suse'
become: true
- name: Check whether the xrdp service account exists
ansible.builtin.command: getent passwd xrdp
register: plasma_xrdp_account
changed_when: false
failed_when: false
become: true
- name: Ensure xrdp user can read the TLS certificate group
ansible.builtin.user:
name: xrdp
groups: ssl-cert
append: true
become: true
when: plasma_xrdp_account.rc == 0
- name: Enable and start XRDP services
ansible.builtin.service:
name: "{{ item }}"
enabled: true
state: started
loop: "{{ plasma_xrdp_services }}"
become: true
- name: Check whether UFW is installed
ansible.builtin.stat:
path: /usr/sbin/ufw
register: plasma_ufw_binary
become: true
- name: Allow XRDP through UFW
ansible.builtin.command: "ufw allow {{ plasma_rdp_port }}/tcp"
register: plasma_ufw_allow
changed_when: "'Skipping adding existing rule' not in plasma_ufw_allow.stdout"
failed_when: false
become: true
when:
- plasma_enable_ufw | bool
- plasma_ufw_binary.stat.exists | default(false)

View File

@ -0,0 +1,16 @@
---
- name: Install minimal Plasma + XRDP stack
ansible.builtin.import_tasks: install.yml
tags: [plasma, plasma_install]
- name: Configure Plasma session and XRDP user setup
ansible.builtin.import_tasks: config.yml
tags: [plasma, plasma_config]
- name: Apply Plasma resource-saving tweaks
ansible.builtin.import_tasks: optimize.yml
tags: [plasma, plasma_optimize]
- name: Validate XRDP desktop readiness
ansible.builtin.import_tasks: validate.yml
tags: [plasma, plasma_validate]

View File

@ -0,0 +1,10 @@
---
- name: Ensure Plasma session settings are persisted
ansible.builtin.file:
path: "{{ plasma_user_home }}/.config"
state: directory
owner: "{{ plasma_user }}"
group: "{{ plasma_user }}"
mode: "0755"
become: true
when: plasma_manage_user | bool

View File

@ -0,0 +1,33 @@
---
- name: Check XRDP service status
ansible.builtin.command: systemctl status xrdp --no-pager --full
register: plasma_xrdp_status
changed_when: false
failed_when: false
become: true
- name: Show XRDP service status
ansible.builtin.debug:
var: plasma_xrdp_status.stdout_lines
- name: Check memory usage
ansible.builtin.command: free -m
register: plasma_memory_usage
changed_when: false
failed_when: false
- name: Show memory usage
ansible.builtin.debug:
var: plasma_memory_usage.stdout_lines
- name: Check current session type if available
ansible.builtin.shell: 'printf "%s\n" "${XDG_SESSION_TYPE:-unknown}"'
args:
executable: /bin/bash
register: plasma_session_type
changed_when: false
failed_when: false
- name: Show session type hint
ansible.builtin.debug:
msg: "XDG_SESSION_TYPE={{ plasma_session_type.stdout | default('unknown') }} (expected x11 inside the RDP session)"

View File

@ -0,0 +1 @@
exec startplasma-x11

View File

@ -0,0 +1,6 @@
---
plasma_user_home: "/home/{{ plasma_user }}"
plasma_xsession_file: "{{ plasma_user_home }}/.xsession"
plasma_xrdp_services:
- xrdp-sesman
- xrdp

View File

@ -12,6 +12,7 @@ Minimal XFCE4 + XRDP role for constrained Ubuntu/Debian VPS hosts.
- Enables `xrdp` and `xrdp-sesman` - Enables `xrdp` and `xrdp-sesman`
- Disables compositor and animations to reduce resource usage - Disables compositor and animations to reduce resource usage
- Optionally opens TCP `3389` with UFW if UFW is present - Optionally opens TCP `3389` with UFW if UFW is present
- Creates or updates the desktop user and ensures it has a usable local password for XRDP login
## Variables ## Variables
@ -22,6 +23,7 @@ Minimal XFCE4 + XRDP role for constrained Ubuntu/Debian VPS hosts.
- `xfce_disable_compositor`: default `true` - `xfce_disable_compositor`: default `true`
- `xfce_disable_animations`: default `true` - `xfce_disable_animations`: default `true`
- `xfce_user_groups`: supplemental groups for the desktop user, default `["sudo"]` - `xfce_user_groups`: supplemental groups for the desktop user, default `["sudo"]`
- `xfce_user_password_plaintext`: required password for the desktop user so XRDP can authenticate
## Example playbook ## Example playbook
@ -40,6 +42,7 @@ Run these on the server after connecting through RDP:
systemctl status xrdp --no-pager --full systemctl status xrdp --no-pager --full
echo "$XDG_SESSION_TYPE" echo "$XDG_SESSION_TYPE"
free -m free -m
passwd -S ubuntu
``` ```
Expected: Expected:
@ -47,3 +50,4 @@ Expected:
- `xrdp` is active - `xrdp` is active
- `XDG_SESSION_TYPE=x11` - `XDG_SESSION_TYPE=x11`
- memory stays under the host budget - memory stays under the host budget
- `ubuntu` is not locked and can authenticate with the password you provided

View File

@ -27,3 +27,4 @@ xfce_manage_user: true
xfce_user_shell: /bin/bash xfce_user_shell: /bin/bash
xfce_user_groups: xfce_user_groups:
- sudo - sudo
xfce_user_password_plaintext: ""

View File

@ -5,9 +5,29 @@
shell: "{{ xfce_user_shell }}" shell: "{{ xfce_user_shell }}"
create_home: true create_home: true
state: present state: present
password_lock: false
become: true become: true
when: xfce_manage_user | bool when: xfce_manage_user | bool
- name: Fail when the desktop user password is not provided
ansible.builtin.assert:
that:
- xfce_user_password_plaintext | length > 0
fail_msg: >-
xfce_user_password_plaintext must be set so XRDP can authenticate the
desktop user.
when: xfce_manage_user | bool
- name: Set desktop user password for XRDP login
ansible.builtin.user:
name: "{{ xfce_user }}"
password: "{{ xfce_user_password_plaintext | password_hash('sha512') }}"
update_password: always
password_lock: false
become: true
no_log: true
when: xfce_manage_user | bool
- name: Ensure the desktop user can sudo - name: Ensure the desktop user can sudo
ansible.builtin.user: ansible.builtin.user:
name: "{{ xfce_user }}" name: "{{ xfce_user }}"

View File

@ -3,7 +3,5 @@
hosts: localhost hosts: localhost
connection: local connection: local
gather_facts: false gather_facts: false
vars_files:
- vars/cloudflare_svc_plus_dns.yml
roles: roles:
- cloudflare_dns - role: cloudflare_svc_plus_dns

View File

@ -0,0 +1,7 @@
---
- name: Update svc.plus Cloudflare DNS records
hosts: localhost
connection: local
gather_facts: false
roles:
- role: cloudflare_svc_plus_dns

View File

@ -42,17 +42,7 @@ cloudflare_dns_records:
proxied: false proxied: false
- type: A - type: A
name: acp-server.svc.plus name: acp-server.svc.plus
content: 167.179.110.129 content: 46.250.251.132
ttl: 1
proxied: false
- type: A
name: acp-server-codex.svc.plus
content: 167.179.110.129
ttl: 1
proxied: false
- type: A
name: acp-server-opencode.svc.plus
content: 167.179.110.129
ttl: 1 ttl: 1
proxied: false proxied: false
- type: CNAME - type: CNAME