1

Host and Client Ubuntu 19.04. OpenSSH

I have a SSH server in my home LAN that I mount to my laptop using the LAN network address. When I'm at my weekend hideaway, I have to change my fstab to mount through my router's external IP address.

My router does not support loopback so I can't use the external IP when I'm in the LAN.

My approach has been to keep two sections in my fstab. One for the LAN and one for the external IP. Then I comment out the fstab lines to suit my location.

I wonder if there is a better way.

I can leave both sections open. I use the nofail option so the boot process is not aborted but the startup is extremely slow. I tried putting ConnectionTimeout=5in the sshfs options but this doesn't improve the boot time.

It's not a critical issue obviously, just a nice-to-have. Some conditional mounting? Or perhaps setting the timeout is not as simple as I've assumed.

Later: After some looking about:

following on suggestions by Archemar and muru, I'm looking into systemd initialization to make a fork to direct the mount process.

My theory (purely speculative based on reading documentation -- no experimentation) is to direct the mount command through the --fstab option to a fstab for the location.

One way to establish location is to read the network device IP. My LAN DHCP will assign 10.0.0.120. Any other IP address strongly suggests I am connecting to some network other than my LAN. Not completely reliable but good enough for non-critical application. If there is a more reliable way I'm happy to hear it.

I'm stuck now on locating the systemd mount service and where in the systemd init that service is called.

I look first in /lib/systemd/. This directory contains mostly systemd executables, and some directories. One of these directories system-generators contains a promising file systemd-fstab-generator. This is an executable so I'm looking for, I guess the conf file for this.

This is as far as I've got so far. I'll come back and post more later, but in the meantime I'd appreciate any tips, warnings, alarms...

  • mount can be done dynamically later after the boot (/etc/init.d is a good place to hold this script), this way you can test/ping/whatever before actual mount. – Archemar Jun 21 '19 at 4:18
  • @Archemar Okay. Ubuntu 19.04 uses systemd and I have a notion that systemd and init.d are not naturally friendly. Could be just nerves, I suppose, but thanks. I'll check that out in the morning. – Stephen Boston Jun 21 '19 at 4:22
  • @StephenBoston the idea can work just as well with systemd (better perhaps, you can have filesystems dependent on other units, and you can have a unit that checks for either network) – muru Jun 21 '19 at 5:09
1

I don't have an equivalent setup to test, but here's how I imagine the setup would work.

First, remove the lines in /etc/fstab for this mount.

Then, you'd have a couple each service and mount that look like the following.

  1. For the internal network:

    /etc/systemd/system/int-ip-check.service:

    [Unit]
    Description=Check for internal network
    Type=oneshot
    Requires=NetworkManager-wait-online.service
    After=NetworkManager-wait-online.service
    OnFailure=ext-ip-check.service
    Conflicts=ext-ip-check.service
    Wants=int-ip.mount
    Before=int-ip.mount
    
    [Service]
    ExecStartPre=<command to check for internal network>
    RemainAfterExit=yes
    
    [Install]
    WantedBy=default.target
    

    /etc/systemd/system/int-ip.mount:

    [Unit]
    Documentation=SSHFS mount (internal IP)
    PartOf=int-ip-check.service
    
    [Mount]
    Where=/some/path
    What=user@int-ip:/some/path
    Type=fuse.sshfs
    Options=noauto,_netdev,...
    
  2. For the external network:

    /etc/systemd/system/ext-ip-check.service:

    [Unit]
    Description=Check for external network
    Type=oneshot
    Requires=NetworkManager-wait-online.service
    After=NetworkManager-wait-online.service
    Wants=ext-ip.mount
    Before=ext-ip.mount
    
    [Service]
    ExecStartPre=<command to check for external network>
    RemainAfterExit=yes
    

    /etc/systemd/system/ext-ip.mount:

    [Unit]
    Documentation=SSHFS mount (external IP)
    PartOf=ext-ip-check.service
    
    [Mount]
    Where=/some/path
    What=user@int-ip:/some/path
    Type=fuse.sshfs
    Options=noauto,_netdev,...
    

So the ordering here is like this:

  1. Both mounts are PartOf their respective check service. So if the check services are stopped or restarted, the mounts are as well.
  2. Both check services want their respective mounts (so starting them will cause the mounts to be started as well), but have the mounts start after they have started.
  3. Both check services require and are started after NetworkManager-wait-online - on Ubuntu this should serve to make them wait till some network device is online.
  4. The check services conflict with each other (if one is started, the other is stopped).
  5. The first check starts the other if it fails.
  6. The internal network check is set to start by default (WantedBy=default.target)
  7. Both checks have RemainAfterExit=yes so that they remain active (and not stopped) after the check command exits (this is needed in this case for Type=oneshot). They have to fail or be stopped for the mounts to be stopped.

The flow then should look like this:

  1. Once the network is online, the internal IP check starts.
  2. If it finishes successfully, the internal mount should start and run fine (or it will get stopped due to the PartOf).
  3. If it fails, the external IP check starts. Correspondingly, the external mount should start and run after the check.

When you switch networks, you probably will be able to start the corresponding check service to have the other get unmounted.

You'll have to play around with the dependencies here, but hopefully this will be a decent starting point.

One potential change: instead of Type=oneshot and RemainAfterExit, you might want to use Type=simple or Type=forking, and maybe have a command that periodically checks whether the network is the correct one (a shell loop, for example).

| improve this answer | |
  • This seems clear enough. If the internal mount fails then flow is directed to the OnFailure target. The ip.mount pieces do the work and all the rest is control. I'm confused by the ExecStartPre though. The logic flows simply, if I understand correcty, but there is something I'm missing here. Can you see what it is? I'm also not clear on how to signal mount failure. – Stephen Boston Jun 28 '19 at 20:44
1

You could do this transparent to the mount-level, and perform the network redirection to the 2 different hostnames on ~/.ssh/config level.

In the ~/.ssh/config for the mounting user:

Match host MyServerAlias exec "<shell command to return true if in local LAN>"
   Hostname Local.IP.or.hostname

Host MyServerAlias
   Hostname external.hostname
   Port <External Port for SSH-Forward on Home Router>

That way, when mounting, you just use one line with the hostname MyServerAlias, and the ssh layer will automagically connect to the right ssh URI.

The Magic lies in the Match keyword, it can chain a series of shell commands, and will apply the lines of the following block only, if the shell chain returned true.

You could use e.g. exec "timeout 0.4 nc -z %h %p 2>/dev/null" in the match line to check, if the local ip&port of MyServerAlias is reachable.

I've been using (a more complicated version with integrated port-knocking of) this for several years now, ever since ssh got match.

With an afuse+sshfs automounter-setup via a Systemd user unit (or via ~/.profile) it's great for use on the laptop. (and no fiddling with fstab)

Here is a blog-post with more info about the Match keyword.

| improve this answer | |
  • This looks simple enough. I'll give it a try this weekend. – Stephen Boston Jul 9 '19 at 10:59
  • @StephenBoston It is! Once you see the big picture, it's just installing afuse&sshfs, copying the unit file to the right place, create ~/scp, issue the 3 commands to activate unit, and add the 5 lines to ~/.ssh/config to activate LocalLAN detection, and use the external hostname otherwise. Then you have on-demand-mounted, auto-routed, user-level, transparent-to-all-apps SFTP goodness :) – Alex Stragies Jul 9 '19 at 11:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.