Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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
#
# Changelog:
# 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
#
# Finally, register service with cygrunsrv and start it
#
cygrunsrv -R sshd || true
cygrunsrv -I 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 sshd` + `net start sshd`
if ! net start sshd; then
echo "ERROR: Unable to start sshd service"
exit 1
fi
@bagong

This comment has been minimized.

Copy link

bagong commented May 21, 2015

Hey, this is cool, I tried it and it worked...
Two things somebody else in my situation might want to know in advance ;) : you need to run the script in an admin shell, and you have to open the windows firewall for sshd (at least I had to).
Another thing I had to do: create a file /etc/passwd by running mkpasswd > /etc/passwd before running the script, otherwise the script wouldn't complete without error. In theory that shouldn't be necessary, but that's how it went for me.
Thanks for this!

@ghost

This comment has been minimized.

Copy link

ghost commented Jun 1, 2015

By default, MSYS2 does not create /etc/passwd, but MSYS2 will use /etc/passwd if it exists (see /etc/nsswitch.conf). The /etc/passwd modifications are needed if and only if you have an existing /etc/passwd. I put an if-guard around that section (I don't use /etc/passwd) and it worked.

See: https://gist.github.com/whatsthepoint/963f4638e5343355570f

@cchamberlain

This comment has been minimized.

Copy link

cchamberlain commented Jul 26, 2015

Worked for me. I already had the /etc/passwd and didn't have to do anything except restart shell as admin.

@samhocevar

This comment has been minimized.

Copy link
Owner Author

samhocevar commented Aug 14, 2015

I need the /etc/passwd file here, otherwise sshd won’t even start, complaining that Privilege separation user sshd does not exist. Did you not run against this issue?

@kankaristo

This comment has been minimized.

Copy link

kankaristo commented Oct 24, 2015

Thanks for the script!

Worked for me on a Windows 10 virtual machine by running the script in an admin MSYS2 shell and allowing SSH in Windows firewall (thanks for pointing this out @bagong). The connection just hangs and eventually times out without opening up the firewall.

I don't have /etc/passwd and it's working fine. Scratch that, I was looking in C:\msys32, but installed the SSH server for C:\msys64...

I'm still having some problems with using an SSH key for authentication, but that could have something to do with setting HOME="/c/Users/$USER", or having a space in the user name (intentionally, to make sure that my scripts don't break because of it).

Edit:
SSH keys work fine too, there's just something wrong with the way I'm changing HOME.

@ghost

This comment has been minimized.

Copy link

ghost commented Jan 23, 2016

@samhocevar, I did not run into that issue.

I just set up, again, sshd on a fresh install of msys2 on a fresh install of Windows 10 (home computer, no domain or anything). I edited /etc/nsswitch.conf, setting "db_home: windows" to unify the home directories, but otherwise it's bog standard defaults. I ran the script (my edit, with the /etc/passwd stuff omitted), and it all worked. I still have no /etc/passwd.

(I did get a complaint about /var/log/lastlog not found, so I just touched it. But that's probably irrelevant.)

@ghost

This comment has been minimized.

Copy link

ghost commented Mar 26, 2016

Did not ran for me.

sushovan@LAPTOP-5755G MINGW64 ~
$ ssh sushovan@localhost
sushovan@localhost's password:
Connection to localhost closed by remote host.
Connection to localhost closed.

sshd.log:
Server listening on :: port 22.
Server listening on 0.0.0.0 port 22.
Accepted password for sushovan from ::1 port 50258 ssh2
chown(/dev/pty1, 197609, 197121) failed: Invalid argument

@sbleon

This comment has been minimized.

Copy link

sbleon commented Jul 5, 2016

@samhocevar this worked great! Thanks!

Has anyone gotten ChrootDirectory working with this setup? I always get fatal: bad ownership or modes for chroot directory component "/". sshd expects / to be owned by root, and there's no root user in this setup.

@aaptel

This comment has been minimized.

Copy link

aaptel commented Oct 31, 2016

@samhocevar the password generated this way (random + tr) sometime don't pass the password complexity requirement enabled by default on Windows (server 2016, probably other windows too I guess) resulting in the net command failing. Might want to add a constant suffix/prefix with uppercase and digits to make it pass the requirement.

@artygus

This comment has been minimized.

Copy link

artygus commented Nov 9, 2016

to start shell as mingw64/mingw32:

  • set PermitUserEnvironment to yes in /etc/ssh/sshd_config
  • add MSYSTEM=MINGW64 to ~/.ssh/environment
@Globik

This comment has been minimized.

Copy link

Globik commented Nov 25, 2016

Is sshd just like cygserver? Can it be using for dynamic shared memory stuff like a shm_open()?

@caedn

This comment has been minimized.

Copy link

caedn commented Apr 7, 2017

This worked great, thank you!

@elieux

This comment has been minimized.

Copy link

elieux commented Apr 7, 2017

Another way to start a specific shell (MSYS/MINGW32/MINGW64):

  • Put AcceptEnv MSYSTEM into /etc/ssh/sshd_config on the remote machine.
  • Put SendEnv MSYSTEM into /etc/ssh/ssh_config or ~/.ssh/config on the local machine.

Then you can connect from MSYS2 to MSYS2 and it will use the same shell, or just prefix your ssh command with MSYSTEM=... .

@AllenKll

This comment has been minimized.

Copy link

AllenKll commented Sep 7, 2017

@sushovan-dw I have the same issue. Did you find a fix?

@carlolars

This comment has been minimized.

Copy link

carlolars commented Sep 11, 2017

Got it to work for my computer and user that is connected to a domain at work. Here are the things I had problems with:

  • passwd -e "${PRIV_USER}" didn't work, removed the line and used the local user and group manager to set the password to never expire.
  • My home-directory is on a network share, when logging on via ssh then it always used /home/username as the home directory so I just created a soft-link in /home/ to my home-directory:
    mklink /D C:\msys64\home\username \\server\home\userhomedir
@rkitover

This comment has been minimized.

Copy link

rkitover commented Sep 19, 2017

Another way to get the right MSYSTEM environment, put the following at the top of your ~/.profile:

if [ -n "$SSH_CONNECTION" ]; then
    export MSYSTEM=MINGW32
    source /etc/profile
fi
@HaskellZhangSong

This comment has been minimized.

Copy link

HaskellZhangSong commented Nov 11, 2017

Windows 10 is installed msys2. This script does not work with me. I set the tmp_pass to be 123 for the all accounts and I tried to login by ssh it refused. (I checked sshd_config, PasswordAuthentication is yes). I tried to login the new privileged account created by the script from Windows with password 123 with success.
I tried to login with username sshd_server and password ssh_server and encounter same problem with @sushovan-dw.
Can someone help

@jouven

This comment has been minimized.

Copy link

jouven commented Dec 2, 2017

Edited, after I had to reinstall windows recently (2018-05-08, original message is from December 2017), I was confused reading my own instructions so I improved them.
1 A user must be created in /etc/passwd (msys2 doesn't create a user by default, and I don't mean the sshd ones created by this script), I found in https://sourceforge.net/p/msys2/discussion/general/thread/76612760/ that doing /usr/bin/mkpasswd prints the windows users as passwd lines. Use your user line from that "output" and paste it in /etc/passwd
2 Then run passwd in the MSYS2 console to give the user a password, this will change your current user password in windows too
3 Create a rule for the /usr/bin/sshd.exe in the windows firewall, for the port configured in /etc/ssh/sshd_config (default is 22)
4 Run a MSYS2 console as admin and run this script, it should tell you that a sshd service has started
5 Reboot?
6 Profit

@abakum

This comment has been minimized.

Copy link

abakum commented Dec 6, 2017

A user must created in /etc/passwd

if [ ! /etc/passwd ]; then
 mkpasswd -c > /etc/passwd
 touch /var/log/lastlog
 echo AcceptEnv MSYSTEM >> ~/etc/ssh/sshd_config
 echo To start shell as ${MSYSTEM} use:
 echo MSYSTEM=${MSYSTEM} ssh ...
fi

@kurtdanielsson

This comment has been minimized.

Copy link

kurtdanielsson commented Feb 16, 2018

Hello! This works good! But be careful:

I accidentally messed upp... Somehow the script did not work, so I started changing stuff, I changed the PRIV_USER to my own computer user, and then the password was changed to a random password, so now I can not access my computer anymore...

Besides from this, I have run it on another computer where it worked very well!

Thanks :)

@imfatant

This comment has been minimized.

Copy link

imfatant commented Mar 22, 2018

If you're a casual Windows 10 user who has admin rights and who hasn't bothered to set a Windows password, then the following method works (bear in mind that there are a couple of unnecessary steps here, but at least this really does work):

1. Run MSYS2 shell as admin.
2. pacman -S --noconfirm openssh cygrunsrv mingw-w64-x86_64-editrights
3. passwd <username>  # Enter a password here.
4. Reboot your computer. Log in this time with the password you just set. Run MSYS2 shell as admin again.
5. ssh-keygen -A
6. ssh-keygen -b 4096
7. :>| /var/log/lastlog
8. mkpasswd -c >/etc/passwd
9. Run the script. Some of the messages look like errors. They aren't; it's just the way the script's been written.

Remember to open port 22 in your Windows firewall.

1. To stop the service, use: net stop sshd
2. To remove the service, use: cygrunsrv -R sshd
3. To stop having to enter a Windows password, run MSYS2 as admin again and use: passwd <username>,  pressing return twice to leave the password blank.
@stephe-ada-guru

This comment has been minimized.

Copy link

stephe-ada-guru commented Apr 8, 2018

Thanks for this script! I have Windows 10 Home, which doesn't have the "run as Admin" option on Start items. But I can run Power Shell as admin, via the Windows-x menu, and run mingw64.exe from there. Then the script runs. After disabling firewall on private network, I can login.

@InBetweenNames

This comment has been minimized.

Copy link

InBetweenNames commented Sep 19, 2018

I'm having the same problem @ghost and @AllenKll are having.

@KenJackson

This comment has been minimized.

Copy link

KenJackson commented Sep 24, 2018

I just used this script successfully on Windows10. But I'm already running Cygwin sshd on the same machine, so I had to make some adjustments:

  • Edited this script to replace "sshd" with "msshd"
  • Edited /etc/ssh/sshd_config to specify a different port (instead of 22)
  • Added a rule to Windows Firewall to allow incoming TCP on that port
  • Picked a new fake hostname
  • edited my .bashrc file to override $HOSTNAME, hostname and uname

These two steps had to be done for MSYS2, Cygwin and for every machine I connect from:

  • Edited /etc/hosts to give the fake hostname the same IP address as the real Windows machine
  • Edited ~/.ssh/config to specify the right port for the new fake hostname

Everything works fine!
The only potential issue I see is that user "sshd" is apparently used by both Cygwin and MSYS2, though it hasn't caused any problem yet.

@rcpao-enmotus

This comment has been minimized.

Copy link

rcpao-enmotus commented Nov 9, 2018

Anyone have a solution to "passwd: unrecoverable error 8646" in Win10 Pro 1809 64-bit?

MSYS ~
# passwd rcpao
New password:
Re-enter new password:
passwd: unrecoverable error 8646
Try again.
New password:
Re-enter new password:
passwd: unrecoverable error 8646
Try again.
New password:
Re-enter new password:
passwd: unrecoverable error 8646

MSYS ~
#

@bblancha

This comment has been minimized.

Copy link

bblancha commented Nov 18, 2018

@InBetweenNames @ghost @AllenKll :
In addition to
# mkpasswd.exe -l > /etc/passwd
I had to do
# mkgroup.exe -l > /etc/group

Then I restarted the sshd service.
# net stop sshd
# net start sshd
after that, I was able to login and no longer saw the chown error in sshd.log

@illwieckz

This comment has been minimized.

Copy link

illwieckz commented Mar 21, 2019

@samhocevar, can you put a license on this script? WTFPL2 is ok. ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.