1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.xrdp;
7 confDir = pkgs.runCommand "xrdp.conf" { preferLocalBuild = true; } ''
8 mkdir $out
9
10 cp ${cfg.package}/etc/xrdp/{km-*,xrdp,sesman,xrdp_keyboard}.ini $out
11
12 cat > $out/startwm.sh <<EOF
13 #!/bin/sh
14 . /etc/profile
15 ${cfg.defaultWindowManager}
16 EOF
17 chmod +x $out/startwm.sh
18
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
25
26 substituteInPlace $out/sesman.ini \
27 --replace LogFile=xrdp-sesman.log LogFile=/dev/null \
28 --replace EnableSyslog=1 EnableSyslog=0
29
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
34 ' $out/sesman.ini
35 '';
36in
37{
38
39 ###### interface
40
41 options = {
42
43 services.xrdp = {
44
45 enable = mkEnableOption "xrdp, the Remote Desktop Protocol server";
46
47 package = mkOption {
48 type = types.package;
49 default = pkgs.xrdp;
50 defaultText = "pkgs.xrdp";
51 description = ''
52 The package to use for the xrdp daemon's binary.
53 '';
54 };
55
56 port = mkOption {
57 type = types.int;
58 default = 3389;
59 description = ''
60 Specifies on which port the xrdp daemon listens.
61 '';
62 };
63
64 sslKey = mkOption {
65 type = types.str;
66 default = "/etc/xrdp/key.pem";
67 example = "/path/to/your/key.pem";
68 description = ''
69 ssl private key path
70 A self-signed certificate will be generated if file not exists.
71 '';
72 };
73
74 sslCert = mkOption {
75 type = types.str;
76 default = "/etc/xrdp/cert.pem";
77 example = "/path/to/your/cert.pem";
78 description = ''
79 ssl certificate path
80 A self-signed certificate will be generated if file not exists.
81 '';
82 };
83
84 defaultWindowManager = mkOption {
85 type = types.str;
86 default = "xterm";
87 example = "xfce4-session";
88 description = ''
89 The script to run when user log in, usually a window manager, e.g. "icewm", "xfce4-session"
90 This is per-user overridable, if file ~/startwm.sh exists it will be used instead.
91 '';
92 };
93
94 };
95 };
96
97
98 ###### implementation
99
100 config = mkIf cfg.enable {
101
102 # xrdp can run X11 program even if "services.xserver.enable = false"
103 xdg = {
104 autostart.enable = true;
105 menus.enable = true;
106 mime.enable = true;
107 icons.enable = true;
108 };
109
110 fonts.enableDefaultFonts = mkDefault true;
111
112 systemd = {
113 services.xrdp = {
114 wantedBy = [ "multi-user.target" ];
115 after = [ "network.target" ];
116 description = "xrdp daemon";
117 requires = [ "xrdp-sesman.service" ];
118 preStart = ''
119 # prepare directory for unix sockets (the sockets will be owned by loggedinuser:xrdp)
120 mkdir -p /tmp/.xrdp || true
121 chown xrdp:xrdp /tmp/.xrdp
122 chmod 3777 /tmp/.xrdp
123
124 # generate a self-signed certificate
125 if [ ! -s ${cfg.sslCert} -o ! -s ${cfg.sslKey} ]; then
126 mkdir -p $(dirname ${cfg.sslCert}) || true
127 mkdir -p $(dirname ${cfg.sslKey}) || true
128 ${pkgs.openssl.bin}/bin/openssl req -x509 -newkey rsa:2048 -sha256 -nodes -days 365 \
129 -subj /C=US/ST=CA/L=Sunnyvale/O=xrdp/CN=www.xrdp.org \
130 -config ${cfg.package}/share/xrdp/openssl.conf \
131 -keyout ${cfg.sslKey} -out ${cfg.sslCert}
132 chown root:xrdp ${cfg.sslKey} ${cfg.sslCert}
133 chmod 440 ${cfg.sslKey} ${cfg.sslCert}
134 fi
135 if [ ! -s /run/xrdp/rsakeys.ini ]; then
136 mkdir -p /run/xrdp
137 ${cfg.package}/bin/xrdp-keygen xrdp /run/xrdp/rsakeys.ini
138 fi
139 '';
140 serviceConfig = {
141 User = "xrdp";
142 Group = "xrdp";
143 PermissionsStartOnly = true;
144 ExecStart = "${cfg.package}/bin/xrdp --nodaemon --port ${toString cfg.port} --config ${confDir}/xrdp.ini";
145 };
146 };
147
148 services.xrdp-sesman = {
149 wantedBy = [ "multi-user.target" ];
150 after = [ "network.target" ];
151 description = "xrdp session manager";
152 restartIfChanged = false; # do not restart on "nixos-rebuild switch". like "display-manager", it can have many interactive programs as children
153 serviceConfig = {
154 ExecStart = "${cfg.package}/bin/xrdp-sesman --nodaemon --config ${confDir}/sesman.ini";
155 ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID";
156 };
157 };
158
159 };
160
161 users.users.xrdp = {
162 description = "xrdp daemon user";
163 isSystemUser = true;
164 group = "xrdp";
165 };
166 users.groups.xrdp = {};
167
168 security.pam.services.xrdp-sesman = { allowNullPassword = true; startSession = true; };
169 };
170
171}