2

We're working towards a zero trust security model in our office, with servers secured behind a WireGuard VPN. We already use WireGuard for remote access, so this is the simplest way to ensure encryption for all network traffic. WireGuard guarantees a specific IP address for each user.

We would like to authenticate file server access with both username and IP address, to prevent lateral movement in the event of a breach. We're using Samba 4.13 with the simple default passdb backend = tdbsam (no domain controller).

I was hoping for a simple solution with PAM, but although the default configuration file still includes auth, I've read that it isn't supported (Samba uses challenge-response authentication, which isn't the PAM way). When I added auth required pam_rhosts.so to /etc/pam.d/samba, there was no change in my ability to authenticate. Adding it to the account or session sections caused authentication to fail, even from the authorized IP address.

Other solutions I can see are to modify the source code or run an Active Directory Domain Controller, but those seem like a much bigger job to implement and maintain. An application firewall might also work, but I'm not aware of anything that can inspect Samba traffic.

Is there a simple way to add this user-host pair restriction (or MFA in general) to Samba 4?

-- Edit --

The /etc/pam.d/samba configuration clearly has some effect, because adding a 'debug' option to any of the lines in the auth section gives debug logging as follows, though additional required lines make no difference to the outcome of authentication:

[2022/11/10 15:56:56.258835,  3] ../../auth/ntlmssp/ntlmssp_server.c:509(ntlmssp_server_preauth)
  Got user=[username] domain=[FILESERVER] workstation=[COMPUTER-NAME] len1=24 len2=190
[2022/11/10 15:56:56.258932,  3] ../../source3/auth/auth.c:200(auth_check_ntlm_password)
  check_ntlm_password:  Checking password for unmapped user [FILESERVER]\[username]@[COMPUTER-NAME] with the new password interface
[2022/11/10 15:56:56.258959,  3] ../../source3/auth/auth.c:203(auth_check_ntlm_password)
  check_ntlm_password:  mapped user is: [FILESERVER]\[username]@[COMPUTER-NAME]
[2022/11/10 15:56:56.259651,  4] ../../source3/auth/check_samsec.c:183(sam_account_ok)
  sam_account_ok: Checking SMB password for user username
[2022/11/10 15:56:56.262124,  3] ../../source3/auth/auth.c:267(auth_check_ntlm_password)
  auth_check_ntlm_password: sam_ignoredomain authentication for user [username] succeeded
[2022/11/10 15:56:56.262230,  4] ../../source3/auth/pampass.c:485(smb_pam_start)
  smb_pam_start: PAM: Init user: username
[2022/11/10 15:56:56.267184,  4] ../../source3/auth/pampass.c:494(smb_pam_start)
  smb_pam_start: PAM: setting rhost to: 10.1.2.3
[2022/11/10 15:56:56.267246,  4] ../../source3/auth/pampass.c:503(smb_pam_start)
  smb_pam_start: PAM: setting tty
[2022/11/10 15:56:56.267285,  4] ../../source3/auth/pampass.c:511(smb_pam_start)
  smb_pam_start: PAM: Init passed for user: username
[2022/11/10 15:56:56.267323,  4] ../../source3/auth/pampass.c:569(smb_pam_account)
  smb_pam_account: PAM: Account Management for User: username
[2022/11/10 15:56:56.267907,  4] ../../source3/auth/pampass.c:588(smb_pam_account)
  smb_pam_account: PAM: Account OK for User: username
[2022/11/10 15:56:56.268550,  4] ../../source3/auth/pampass.c:467(smb_pam_end)
  smb_pam_end: PAM: PAM_END OK.
[2022/11/10 15:56:56.268641,  2] ../../source3/auth/auth.c:323(auth_check_ntlm_password)
  check_ntlm_password:  authentication for user [username] -> [username] -> [username] succeeded
Roger Dueck
  • 131
  • 5
  • 17

3 Answers3

2

I'm not keen on this particular topic, but after adding auth required pam_rhosts.so, you did create the respective ~/.rhosts files right?

This link says the rhosts files need to specify the hostname [username] pair(s) that will be permitted, I'm assuming ip addresses would work just as well in place of hostname.

Are there any specific log messages you can share (redacted if needs be of course) that might give more insight?


EDIT - No idea if this will work, but you could maybe try with netgroups.

I'm not sure of the requirements or procedure for setting these up or if it's as simple as just creating & populating the /etc/netgroup file.

Then based on the example in the manpage for pam_succeed_if, it seems like you could make netgroups for the (host,user) pairs, which I imagine could look something like this:

sambang (alicepc, alice) (bobpc, bob) (charliepc, charlie)

and add this module to your samba auth to make pam auth succeed if the (host,user) pair is in the netgroup, something like:

auth required pam_succeed_if.so audit user innetgr netgroup sambang

I'd probably replace audit with debug until you get it working (assuming it can work).

A. Trevelyan
  • 478
  • 1
  • 10
  • Yes, I did create `~/.rhosts`, and it connects just as before, even from hosts not listed in `~/.rhosts`. No significant error messages, just 2 lines in `/var/log/syslog`: Nov 10 14:45:12 fileserver smbd[443420]: [2022/11/10 14:45:12.693848, 0] ../../source3/rpc_server/mdssvc/mdssvc_es.c:272(mds_es_http_connect_done) \nNov 10 14:45:12 fileserver smbd[443420]: mds_es_http_connect_done: HTTP connect failed, retrying... – Roger Dueck Nov 10 '22 at 21:46
  • Got an idea but it's basically a shot in the dark, will update my original post just to make it easier to read. – A. Trevelyan Nov 10 '22 at 22:03
  • Adding the `pam_succeed_if` module made no difference. It does seem that the `auth` directives in `/etc/pam.d/samba` are ignored in Samba 4. I did find something interesting in `/var/log/samba/`: `check_ntlm_password: Checking password for unmapped user [FILESERVER]\[username]@[COMPUTER-NAME] with the new password interface`. Maybe there's a way to modify how `check_ntml_password` works. – Roger Dueck Nov 10 '22 at 23:11
  • Seems like it could be good to look into considering its seeing the `[username]@[COMPUTER-NAME]`, not much support I can provide though, I was already out of my depth here & I'm caught up in something else now. Good luck. – A. Trevelyan Nov 10 '22 at 23:41
1

Wouldn't samba's ACL per IP/subnet files /etc/hosts.allow & /etc/hosts.deny be enough to restrict shared mounts to users?

See this article describing how to set up samba's authorisations.

EDIT: Also, here is an answer for PAM modules for roaming home folders. Maybe it's the same use-case as yours?

FWIW, I'd give a try on Hashicorp's Boundary for a 0-trust solution.

Marcel
  • 1,730
  • 10
  • 15
  • 1
    I think what he's going for here is he wants individual users to only be able to log in from a specific IP which is dictated by WireGuard. I believe those files would just specify a general blanket of which remote addresses can access a share. – A. Trevelyan Nov 10 '22 at 04:15
  • From OP's question: "we would like to authenticate file server access". That's how you do it with TCP wrappers to allow only specific subnets, the users are already authorised into the server through Wireguard, no? In any case, maybe the simplest solution is already enough? Why don't we let the OP first test the TCP wrappers, instead of Pluggable Authentication Modules, instead of trying to interpret his question or rather my answer ;) ? – Marcel Nov 10 '22 at 09:41
  • @A.Trevelyan is correct. `/etc/hosts.allow` won't help, because access is already limited to the WireGuard subnet via `iptables` firewall. What I want to do is require that user `bob` can only connect from `10.1.2.3`, user `sally` can only connect from `10.1.2.4`, etc. So if Sally's computer is compromised, the attacker cannot access Bob's account - lateral movement is restricted. I assume that cracking Samba passwords is *much* easier than cracking a WireGuard connection. Would HCP Boundary solve this problem, or just add another layer before Samba's regular password auth? – Roger Dueck Nov 10 '22 at 16:14
  • I'm sorry if I don't understand you correctly, but from what I understood you ALREADY have people connected to your network via Wireguard and now you want to restrict access to the File Server, it is now irrelevant that they come via a distinct subnet, as they are already in, or what else am I missing? – Marcel Nov 10 '22 at 16:33
  • 1
    From a zero-trust perspective it makes sense to me. The people connected to wireguard (distinct subnet) can all access the file server, say `Alice`, `Bob`, `Charlie` -- They all have shares on the File server. If `Bob`'s machine is compromised, that machine could potentially be used to gain access to all 3 shares on the file server. What he wants is to restrict it such that in that scenario, only `Bob`'s share will be at risk -- ie. `Bob@BobPC` can only access `//Bob`, `Alice@AlicePC` can only access `//Alice`. `Alice@BobPC` cannot access `//Alice` – A. Trevelyan Nov 10 '22 at 19:17
  • To clarify on @A.Trevelyan's comment, this is not a question about user home folders. Only `Bob` can login from `BobPC`, and only `Alice` can login from `AlicePC`. In our use case, they both have access to `//server`, but their privileges on sub-directories differ, i.e. `Bob` has read-write access to `//server/IT` while `Alice` has read-only access to `//server/IT`, but `Alice` has full access to `//server/Accounting` while `Bob` has none. – Roger Dueck Nov 14 '22 at 15:11
  • So from a 0-trust perspective you don't want Bob to loose his MFA device and ask Alice to look his files for him, but she won't be able to as she might be compromised, albeit already connected to read only folders... I don't think samba ever had 0-trust in mind (or any other software FTM, 0-trust is a hype). Just use plain unix permissions and that should do the trick. – Marcel Nov 14 '22 at 15:48
0

I decided to monitor user sessions with smbstatus. This doesn't happen immediately at login, so it won't prevent someone from logging in, but it will terminate an invalid session within a few seconds. Not a perfect solution, but it should be adequate in our case.

Here's how I implemented it in PHP:

#!/usr/bin/php
<?php
/**
@file blockInvalidSambaUserHosts.php
Block invalid user-host pairs from using Samba.
Run as a root service.
Configure valid pairs in /etc/samba/validUserHosts.conf with lines like:
alice 10.1.0.100
bob 10.1.0.101,10.2.0.88
**/

$STATUS_COMMAND = <<< 'END'
smbstatus -p | awk '/SMB3/{print $1" "$2" "$4}'
END;

$configFile = '/etc/samba/validUserHosts.conf';
if (!file_exists($configFile)) file_put_contents($configFile,'');

list($configTime,$validUserHosts) = getConfig($configFile);

while (true) {
    $connections = explode("\n",trim(shell_exec($STATUS_COMMAND)));
    foreach ($connections as $c) {
        if (!$c) continue;
        list($pid,$uname,$ip) = explode(' ',$c);

        if (!isset($validUserHosts[$uname])) $msg = "Invalid user $uname.";
        elseif (!in_array($ip,$validUserHosts[$uname])) $msg = "Invalid address $ip for user $uname.";
        else continue;

        error_log($msg);
        killProcess($ip);
        blockIPAddress($ip,$uname);
        //notify($ip,$uname);
    }

    sleep(10);

    // Reload config if it has been modified
    if (filemtime($configFile) > $configTime) {
        list($configTime,$validUserHosts) = getConfig($configFile);
    }
}

// Immediately stop any operations in progress
function killProcess(string $ip): void {
    shell_exec("kill -9 $ip");
    error_log("Killed smbd process for $ip.");
}

// Immediately block further traffic
function blockIPAddress(string $ip, string $uname): void {
    shell_exec("iptables -I INPUT -s $ip -j DROP -m comment --comment '$uname@$ip not valid for Samba'");
    error_log("All traffic from $ip is now blocked.");
}

function getConfig($configFile) {
    $utime = filemtime($configFile);
    $lines = file($configFile);
    $validUserHosts = [];
    foreach ($lines as $line) {
        list($uname,$hosts) = explode(' ',rtrim($line));
        $validUserHosts[$uname] = explode(',',$hosts);
    }

    return [$utime,$validUserHosts];
}

with a systemd configuration like the following:

# @file blockInvalidSambaUserHosts.service
# @version 1.0.0
[Unit]
Description=Block invalid user-host pairs from using Samba
After=smbd.service

[Service]
Type=simple
ExecStart=/root/blockInvalidSambaUserHosts.php
StandardOutput=append:/var/log/sambaUserHostBlocks.log
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
Roger Dueck
  • 131
  • 5
  • 17