1 { config, lib, pkgs, ... }:
6 cfg = config.services.xrdp;
7 confDir = pkgs.runCommand "xrdp.conf" { preferLocalBuild = true; } ''
10 cp ${cfg.package}/etc/xrdp/{km-*,xrdp,sesman,xrdp_keyboard}.ini $out
12 cat > $out/startwm.sh <<EOF
15 ${cfg.defaultWindowManager}
17 chmod +x $out/startwm.sh
19 substituteInPlace $out/xrdp.ini \
20 --replace "#rsakeys_ini=" "rsakeys_ini=/run/xrdp/rsakeys.ini" \
21 --replace "certificate=" "certificate=${cfg.sslCert}" \
22 --replace "key_file=" "key_file=${cfg.sslKey}" \
23 --replace LogFile=xrdp.log LogFile=/dev/null \
24 --replace EnableSyslog=true EnableSyslog=false
26 substituteInPlace $out/sesman.ini \
27 --replace LogFile=xrdp-sesman.log LogFile=/dev/null \
28 --replace EnableSyslog=1 EnableSyslog=0
30 # Ensure that clipboard works for non-ASCII characters
31 sed -i -e '/.*SessionVariables.*/ a\
32 LANG=${config.i18n.defaultLocale}\
33 LOCALE_ARCHIVE=${config.i18n.glibcLocales}/lib/locale/locale-archive
45 enable = mkEnableOption (lib.mdDoc "xrdp, the Remote Desktop Protocol server");
50 defaultText = literalExpression "pkgs.xrdp";
51 description = lib.mdDoc ''
52 The package to use for the xrdp daemon's binary.
59 description = lib.mdDoc ''
60 Specifies on which port the xrdp daemon listens.
64 openFirewall = mkOption {
67 description = lib.mdDoc "Whether to open the firewall for the specified RDP port.";
72 default = "/etc/xrdp/key.pem";
73 example = "/path/to/your/key.pem";
74 description = lib.mdDoc ''
76 A self-signed certificate will be generated if file not exists.
82 default = "/etc/xrdp/cert.pem";
83 example = "/path/to/your/cert.pem";
84 description = lib.mdDoc ''
86 A self-signed certificate will be generated if file not exists.
90 defaultWindowManager = mkOption {
93 example = "xfce4-session";
94 description = lib.mdDoc ''
95 The script to run when user log in, usually a window manager, e.g. "icewm", "xfce4-session"
96 This is per-user overridable, if file ~/startwm.sh exists it will be used instead.
103 defaultText = literalMD "generated from configuration";
104 description = lib.mdDoc "The location of the config files for xrdp.";
110 ###### implementation
112 config = mkIf cfg.enable {
114 networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];
116 # xrdp can run X11 program even if "services.xserver.enable = false"
118 autostart.enable = true;
124 fonts.enableDefaultFonts = mkDefault true;
128 wantedBy = [ "multi-user.target" ];
129 after = [ "network.target" ];
130 description = "xrdp daemon";
131 requires = [ "xrdp-sesman.service" ];
133 # prepare directory for unix sockets (the sockets will be owned by loggedinuser:xrdp)
134 mkdir -p /tmp/.xrdp || true
135 chown xrdp:xrdp /tmp/.xrdp
136 chmod 3777 /tmp/.xrdp
138 # generate a self-signed certificate
139 if [ ! -s ${cfg.sslCert} -o ! -s ${cfg.sslKey} ]; then
140 mkdir -p $(dirname ${cfg.sslCert}) || true
141 mkdir -p $(dirname ${cfg.sslKey}) || true
142 ${pkgs.openssl.bin}/bin/openssl req -x509 -newkey rsa:2048 -sha256 -nodes -days 365 \
143 -subj /C=US/ST=CA/L=Sunnyvale/O=xrdp/CN=www.xrdp.org \
144 -config ${cfg.package}/share/xrdp/openssl.conf \
145 -keyout ${cfg.sslKey} -out ${cfg.sslCert}
146 chown root:xrdp ${cfg.sslKey} ${cfg.sslCert}
147 chmod 440 ${cfg.sslKey} ${cfg.sslCert}
149 if [ ! -s /run/xrdp/rsakeys.ini ]; then
151 ${cfg.package}/bin/xrdp-keygen xrdp /run/xrdp/rsakeys.ini
157 PermissionsStartOnly = true;
158 ExecStart = "${cfg.package}/bin/xrdp --nodaemon --port ${toString cfg.port} --config ${cfg.confDir}/xrdp.ini";
162 services.xrdp-sesman = {
163 wantedBy = [ "multi-user.target" ];
164 after = [ "network.target" ];
165 description = "xrdp session manager";
166 restartIfChanged = false; # do not restart on "nixos-rebuild switch". like "display-manager", it can have many interactive programs as children
168 ExecStart = "${cfg.package}/bin/xrdp-sesman --nodaemon --config ${cfg.confDir}/sesman.ini";
169 ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID";
176 description = "xrdp daemon user";
180 users.groups.xrdp = {};
182 security.pam.services.xrdp-sesman = { allowNullPassword = true; startSession = true; };