Skip to content

Instantly share code, notes, and snippets.

@samhocevar
Last active January 13, 2024 23:40
Show Gist options
  • Star 97 You must be signed in to star a gist
  • Fork 40 You must be signed in to fork a gist
  • Save samhocevar/00eec26d9e9988d080ac to your computer and use it in GitHub Desktop.
Save samhocevar/00eec26d9e9988d080ac to your computer and use it in GitHub Desktop.
Configure sshd on MSYS2 and run it as a Windows service
#!/bin/sh
#
# msys2-sshd-setup.sh — configure sshd on MSYS2 and run it as a Windows service
#
# Please report issues and/or improvements to Sam Hocevar <sam@hocevar.net>
#
# Prerequisites:
# — MSYS2 itself: http://sourceforge.net/projects/msys2/
# — admin tools: pacman -S openssh cygrunsrv mingw-w64-x86_64-editrights
#
# This script is a cleaned up and improved version of the procedure initially
# found at https://ghc.haskell.org/trac/ghc/wiki/Building/Windows/SSHD
#
# Gotchas:
# — the log file will be /var/log/msys2_sshd.log
# — if you get error “sshd: fatal: seteuid XXX : No such device or address”
# in the logs, try “passwd -R” (with admin privileges)
#
# Changelog:
# 27 Jun 2019 — rename service to msys2_sshd to avoid conflicts with Windows OpenSSH
# — use mkgroup.exe as suggested in the comments
# — fix a problem with CRLF and grep
# 24 Aug 2015 — run server with -e to redirect logs to /var/log/sshd.log
#
set -e
#
# Configuration
#
PRIV_USER=sshd_server
PRIV_NAME="Privileged user for sshd"
UNPRIV_USER=sshd # DO NOT CHANGE; this username is hardcoded in the openssh code
UNPRIV_NAME="Privilege separation user for sshd"
EMPTY_DIR=/var/empty
#
# Check installation sanity
#
if ! /mingw64/bin/editrights -h >/dev/null; then
echo "ERROR: Missing 'editrights'. Try: pacman -S mingw-w64-x86_64-editrights."
exit 1
fi
if ! cygrunsrv -v >/dev/null; then
echo "ERROR: Missing 'cygrunsrv'. Try: pacman -S cygrunsrv."
exit 1
fi
if ! ssh-keygen -A; then
echo "ERROR: Missing 'ssh-keygen'. Try: pacman -S openssh."
exit 1
fi
#
# The privileged cyg_server user
#
# Some random password; this is only needed internally by cygrunsrv and
# is limited to 14 characters by Windows (lol)
tmp_pass="$(tr -dc 'a-zA-Z0-9' < /dev/urandom | dd count=14 bs=1 2>/dev/null)"
# Create user
add="$(if ! net user "${PRIV_USER}" >/dev/null; then echo "//add"; fi)"
if ! net user "${PRIV_USER}" "${tmp_pass}" ${add} //fullname:"${PRIV_NAME}" \
//homedir:"$(cygpath -w ${EMPTY_DIR})" //yes; then
echo "ERROR: Unable to create Windows user ${PRIV_USER}"
exit 1
fi
# Add user to the Administrators group if necessary
admingroup="$(mkgroup -l | awk -F: '{if ($2 == "S-1-5-32-544") print $1;}')"
if ! (net localgroup "${admingroup}" | grep -q '^'"${PRIV_USER}"'\>'); then
if ! net localgroup "${admingroup}" "${PRIV_USER}" //add; then
echo "ERROR: Unable to add user ${PRIV_USER} to group ${admingroup}"
exit 1
fi
fi
# Infinite passwd expiry
passwd -e "${PRIV_USER}"
# set required privileges
for flag in SeAssignPrimaryTokenPrivilege SeCreateTokenPrivilege \
SeTcbPrivilege SeDenyRemoteInteractiveLogonRight SeServiceLogonRight; do
if ! /mingw64/bin/editrights -a "${flag}" -u "${PRIV_USER}"; then
echo "ERROR: Unable to give ${flag} rights to user ${PRIV_USER}"
exit 1
fi
done
#
# The unprivileged sshd user (for privilege separation)
#
add="$(if ! net user "${UNPRIV_USER}" >/dev/null; then echo "//add"; fi)"
if ! net user "${UNPRIV_USER}" ${add} //fullname:"${UNPRIV_NAME}" \
//homedir:"$(cygpath -w ${EMPTY_DIR})" //active:no; then
echo "ERROR: Unable to create Windows user ${PRIV_USER}"
exit 1
fi
#
# Add or update /etc/passwd entries
#
touch /etc/passwd
for u in "${PRIV_USER}" "${UNPRIV_USER}"; do
sed -i -e '/^'"${u}"':/d' /etc/passwd
SED='/^'"${u}"':/s?^\(\([^:]*:\)\{5\}\).*?\1'"${EMPTY_DIR}"':/bin/false?p'
mkpasswd -l -u "${u}" | sed -e 's/^[^:]*+//' | sed -ne "${SED}" \
>> /etc/passwd
done
mkgroup.exe -l > /etc/group
#
# Finally, register service with cygrunsrv and start it
#
cygrunsrv -R msys2_sshd || true
cygrunsrv -I msys2_sshd -d "MSYS2 sshd" -p \
/usr/bin/sshd.exe -a "-D -e" -y tcpip -u "${PRIV_USER}" -w "${tmp_pass}"
# The SSH service should start automatically when Windows is rebooted. You can
# manually restart the service by running `net stop msys2_sshd` + `net start msys2_sshd`
if ! net start msys2_sshd; then
echo "ERROR: Unable to start msys2_sshd service"
exit 1
fi
@hikikomori4
Copy link

hikikomori4 commented Jun 20, 2019

Q: How I can view windows root (disk C,D, others)? In cygwin default access here: /cygdrive/c$ Where its in msys2???
A: cd /c/ to access C: cd /d/ for D:

But why there are no ready symlinks for a quick go? Like in CygWin?

@bk90
Copy link

bk90 commented Nov 4, 2019

@samhocevar I ran into the seteuid issue but could not fix it with the passwd -R method. I found this thread and finally solved the issue by

  • running sshd as the SYSTEM user
  • removing /etc/passwd and /etc/group

Now I can log in again with local and LDAP accounts. Both the additional privileged user and the files seem to have been workarounds which are no longer necessary and even problematic nowadays. I'm not sure wether the unprivileged sshd user is still necessary, but you could completely remove the PRIV_USER, /etc/passwd and /etc/group parts from the script.

@samhocevar
Copy link
Author

@bk90 thanks for the feedback; this script is indeed in need of some cleanup, I will test your suggestions.

@elieux
Copy link

elieux commented Apr 16, 2020

@samhocevar, I made the changes mentioned by @bk90 and published an updated version of the script here: https://www.msys2.org/wiki/Setting-up-SSHd/

@kolewu
Copy link

kolewu commented May 8, 2020

@elieux the script in msys-wiki is missing the generation of tmp_pass. What's more, it still contains the unnecessary PRIV_USER.

@elieux
Copy link

elieux commented May 9, 2020

Thanks, @kolewu. I don't know how I missed this. Should be fixed in a few minutes.

@ylluminate
Copy link

Shouldn't this be renamed from gistfile1.sh to msys2-sshd-setup.sh?

@kolewu
Copy link

kolewu commented May 9, 2020

@elieux Thanks for the quick correction. There is an important information that should be added somewhere (but I don't know where): This configuration of sshd only works for local user. It's not possible to login as domain user (this produces the seteuid fatal error) and I haven't found the missing bits and pieces (passwd -R isn't enough).

@jmgbsas
Copy link

jmgbsas commented Aug 24, 2020

For me the more practical solution was in this link
the section Setting up SSH , without user with privileg...for my desktop....
the script do not run for me ...many issues, thanks
https://www.booleanworld.com/get-unix-linux-environment-windows-msys2/

@dgleba
Copy link

dgleba commented Aug 18, 2021

thanks! this worked for me.

@dmikushin
Copy link

dmikushin commented Nov 25, 2021

There is an important information that should be added somewhere (but I don't know where): This configuration of sshd only works for local user. It's not possible to login as domain user (this produces the seteuid fatal error) and I haven't found the missing bits and pieces (passwd -R isn't enough).

@kolewu Thanks for a very helpful note. Apparently this is solved in https://github.com/rkitover/windows-alt-sshd-msys2

@wapiti96
Copy link

I've tried several different versions of this script including this one, https://www.msys2.org/wiki/Setting-up-SSHd/, and https://github.com/rkitover/windows-alt-sshd-msys2/blob/master/msys2-alt-sshd-setup.sh. All have issues.

When I run this script I get the below error:
"The user name could not be found.

More help is available by typing NET HELPMSG 2221.

The option //ADD is unknown.

The syntax of this command is:

NET USER
[username [password | *] [options]] [/DOMAIN]
username {password | *} /ADD [options] [/DOMAIN]
username [/DELETE] [/DOMAIN]
username [/TIMES:{times | ALL}]
username [/ACTIVE: {YES | NO}]

More help is available by typing NET HELPMSG 3506.

ERROR: Unable to create Windows user "

How do I get around this? I tried jouven's instructions. I ran /usr/bin/mkpasswd | grep using my personal account and copied that line into /etc/passwd. I already have a password on my account, but still tried passwd and it said "You may not change the password for ?. I thought that was fine so I continued on and ran the script and got the error above. Any ideas? Thanks

@rkitover
Copy link

@wapiti96 The script uses //ADD to tell the msys2 path handling converter to not treat it as a path and convert it to /ADD.

Make sure you are not setting the environment variable MSYS2_ARG_CONV_EXCL or other variables related to path conversion.

@Kreijstal
Copy link

if you want to execute a windows app say notepad you are running windows service you have to use a weird hack like this

MSYS2_ARG_CONV_EXCL="*" schtasks /create /sc once /tn "NotepadIn10Sec" /tr "notepad.exe" /st  $(date -d @"$((current_time=$(date +%s) +40 ))" +"%H:%M:%S")  && ( sleep 50;  MSYS2_ARG_CONV_EXCL="*" schtasks /delete /tn "NotepadIn10Sec" /f)

assuming you're logged in as the same user

@Kreijstal
Copy link

and in case you're on a firewall:

cygrunsrv -R msys2_autossh ; cygrunsrv -I msys2_autossh -d "MSYS2 autossh" -p "/usr/bin/bash.exe" -a "autossh -M 0 -i /home/myuser/.ssh/id_rsa -o UserKnownHostsFile=/home/myuser/.ssh/known_hosts -R 9090:localhost:22 remoteuser@192.168.59.30 -N" -y msys2_sshd

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment