Create a gist now

Instantly share code, notes, and snippets.

Embed
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.

Show comment
Hide comment
@bagong

bagong 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!

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.

Show comment
Hide comment
@ghost

ghost 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

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.

Show comment
Hide comment
@cchamberlain

cchamberlain Jul 26, 2015

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

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.

Show comment
Hide comment
@samhocevar

samhocevar 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?

Owner

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.

Show comment
Hide comment
@kankaristo

kankaristo 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.

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.

Show comment
Hide comment
@ghost

ghost 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 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.

Show comment
Hide comment
@ghost

ghost 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

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.

Show comment
Hide comment
@sbleon

sbleon 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.

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.

Show comment
Hide comment
@aaptel

aaptel 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.

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.

Show comment
Hide comment
@artygus

artygus Nov 9, 2016

to start shell as mingw64/mingw32:

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

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.

Show comment
Hide comment
@Globik

Globik Nov 25, 2016

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

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.

Show comment
Hide comment
@caedn

caedn Apr 7, 2017

This worked great, thank you!

caedn commented Apr 7, 2017

This worked great, thank you!

@elieux

This comment has been minimized.

Show comment
Hide comment
@elieux

elieux 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=....

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.

Show comment
Hide comment
@AllenKll

AllenKll Sep 7, 2017

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

AllenKll commented Sep 7, 2017

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

@carlolars

This comment has been minimized.

Show comment
Hide comment
@carlolars

carlolars 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

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.

Show comment
Hide comment
@rkitover

rkitover 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

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.

Show comment
Hide comment
@HaskellZhangSong

HaskellZhangSong 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

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.

Show comment
Hide comment
@jouven

jouven 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

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.

Show comment
Hide comment
@abakum

abakum 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

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.

Show comment
Hide comment
@kurtdanielsson

kurtdanielsson 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 :)

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.

Show comment
Hide comment
@imfatant

imfatant 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. You'll have to log in this time with the password you just set.
  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 they're 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.

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. You'll have to log in this time with the password you just set.
  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 they're 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.

Show comment
Hide comment
@stephe-ada-guru

stephe-ada-guru 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.

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.

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