Samba: Difference between revisions

imported>Jaculabilis
m Add smbpasswd command needed to access shares when setting up the smb server
No edit summary
 
(42 intermediate revisions by 32 users not shown)
Line 1: Line 1:
This guide will help you on how to use samba on nixos.
This guide will help you on how to use samba on nixos.
== Usershares ==
You can allow some users to share via samba a given directory simply via a right click in their file browser (tested with Dolphin). For that, first add this configuration (make sure to add your user in the samba group):
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
{ pkgs, config, ... }: {
  services.samba = {
    # The full package is needed to register mDNS records (for discoverability), see discussion in
    # https://gist.github.com/vy-let/a030c1079f09ecae4135aebf1e121ea6
    package = pkgs.samba4Full;
    usershares.enable = true;
    enable = true;
    openFirewall = true;
    };
  };
  # To be discoverable with windows
  services.samba-wsdd = {
    enable = true;
    openFirewall = true;
  };
  # Make sure your user is in the samba group
  users.users.YOURUSER = {
    isNormalUser = true;
    extraGroups = [ "samba" ];
  };
}
</nowiki>}}
Then, logout and login (to make sure your group change has been taken into account), open Dolphin, right click on a folder you'd like to share, go to Properties, Tab "Share", and configure it the way you want.
== Server setup ==
Example setup for creating a public guest share called <code>public</code> and a private share called <code>private</code>.
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
services.samba = {
  enable = true;
  openFirewall = true;
  settings = {
    global = {
      "workgroup" = "WORKGROUP";
      "server string" = "smbnix";
      "netbios name" = "smbnix";
      "security" = "user";
      #"use sendfile" = "yes";
      #"max protocol" = "smb2";
      # note: localhost is the ipv6 localhost ::1
      "hosts allow" = "192.168.0. 127.0.0.1 localhost";
      "hosts deny" = "0.0.0.0/0";
      "guest account" = "nobody";
      "map to guest" = "bad user";
    };
    "public" = {
      "path" = "/mnt/Shares/Public";
      "browseable" = "yes";
      "read only" = "no";
      "guest ok" = "yes";
      "create mask" = "0644";
      "directory mask" = "0755";
      "force user" = "username";
      "force group" = "groupname";
    };
    "private" = {
      "path" = "/mnt/Shares/Private";
      "browseable" = "yes";
      "read only" = "no";
      "guest ok" = "no";
      "create mask" = "0644";
      "directory mask" = "0755";
      "force user" = "username";
      "force group" = "groupname";
    };
  };
};
services.samba-wsdd = {
  enable = true;
  openFirewall = true;
};
services.avahi = {
  publish.enable = true;
  publish.userServices = true;
  # ^^ Needed to allow samba to automatically register mDNS records (without the need for an `extraServiceFile`
  nssmdns4 = true;
  # ^^ Not one hundred percent sure if this is needed- if it aint broke, don't fix it
  enable = true;
  openFirewall = true;
};
networking.firewall.enable = true;
networking.firewall.allowPing = true;
</nowiki>}}
The <code>samba-wsdd</code> service and avahi is used to advertise the shares to Windows hosts.
=== User Authentication ===
For a user called <code>my_user</code>to be authenticated on the samba server, you can add a password using:
<syntaxhighlight lang="bash">
sudo smbpasswd -a my_user
</syntaxhighlight>
To automate creation of the samba user and the required system user, you can use [https://search.nixos.org/options?show=system.activationScripts system.activationScripts]:
<syntaxhighlight lang="nix">
{
  # Make the samba user "my_user" on the system
  users.users.my_user = {
    description = "Write-access to samba media shares";
    # Add this user to a group with permission to access the expected files
    extraGroups = [ "users" ];
    # Password can be set in clear text with a literal string or from a file.
    # Using sops-nix we can use the same file so that the system user and samba
    # user share the same credential (if desired).
    hashedPasswordFile = config.sops.secrets.samba.path;
    isNormalUser = true;
  };
  # Set "my_user" as a valid samba login
  services.samba = {
    enable = true;
    securityType = "user";
    openFirewall = true;
    settings.my_share_directory = {
      # ...
      "valid users" = "my_user";
    };
  };
  # Activation scripts run every time nixos switches build profiles. So if you're
  # pulling the user/samba password from a file then it will be updated during
  # nixos-rebuild. Again, in this example we're using sops-nix with a "samba" entry
  # to avoid cleartext password, but this could be replaced with a static path.
  system.activationScripts = {
    # The "init_smbpasswd" script name is arbitrary, but a useful label for tracking
    # failed scripts in the build output. An absolute path to smbpasswd is necessary
    # as it is not in $PATH in the activation script's environment. The password
    # is repeated twice with newline characters as smbpasswd requires a password
    # confirmation even in non-interactive mode where input is piped in through stdin.
    init_smbpasswd.text = ''
      /run/current-system/sw/bin/printf "$(/run/current-system/sw/bin/cat ${config.sops.secrets.samba.path})\n$(/run/current-system/sw/bin/cat ${config.sops.secrets.samba.path})\n" | /run/current-system/sw/bin/smbpasswd -sa my_user
    '';
  };
}
</syntaxhighlight>
=== Configuration ===
==== Apple Time Machine ====
In addition to the example above, add this to your configuration:
<syntaxhighlight lang="nix">
services.samba = {
  settings = {
    "tm_share" = {
        "path" = "/mnt/Shares/tm_share";
        "valid users" = "username";
        "public" = "no";
        "writeable" = "yes";
        "force user" = "username";
        # Below are the most imporant for macOS compatibility
        # Change the above to suit your needs
        "fruit:aapl" = "yes";
        "fruit:time machine" = "yes";
        "vfs objects" = "catia fruit streams_xattr";
    };
  };
};
# Ensure Time Machine can discover the share without `tmutil`
services.avahi = {
  extraServiceFiles = {
    timemachine = ''
      <?xml version="1.0" standalone='no'?>
      <!DOCTYPE service-group SYSTEM "avahi-service.dtd">
      <service-group>
        <name replace-wildcards="yes">%h</name>
        <service>
          <type>_smb._tcp</type>
          <port>445</port>
        </service>
          <service>
          <type>_device-info._tcp</type>
          <port>0</port>
          <txt-record>model=TimeCapsule8,119</txt-record>
        </service>
        <service>
          <type>_adisk._tcp</type>
          <!--
            change tm_share to share name, if you changed it.
          -->
          <txt-record>dk0=adVN=tm_share,adVF=0x82</txt-record>
          <txt-record>sys=waMa=0,adVF=0x100</txt-record>
        </service>
      </service-group>
    '';
  };
};
</syntaxhighlight>
==== Printer sharing ====
<syntaxhighlight lang=nix>
services.samba.package = pkgs.sambaFull;
</syntaxhighlight>
A printer share that allows printing to all members in the local network
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
services.samba = {
  enable = true;
  package = pkgs.sambaFull;
  openFirewall = true;
  settings = {
    "global" = {
      "load printers" = "yes";
      "printing" = "cups";
      "printcap name" = "cups";
    };
    "printers" = {
      "comment" = "All Printers";
      "path" = "/var/spool/samba";
      "public" = "yes";
      "browseable" = "yes";
      # to allow user 'guest account' to print.
      "guest ok" = "yes";
      "writable" = "no";
      "printable" = "yes";
      "create mode" = 0700;
    };
  };
};
systemd.tmpfiles.rules = [
  "d /var/spool/samba 1777 root root -"
];
</nowiki>}}
The `samba` packages comes without [[Printing|CUPS printing]] support compiled in, however `sambaFull` features printer sharing support.
==== Active Directory Domain Controller ====
We will setup an AD DC just like the the [https://wiki.samba.org/index.php/Setting_up_Samba_as_an_Active_Directory_Domain_Controller Samba Wiki].
Let's add the following nix config, updating the <code>adDomain</code>, <code>adWorkgroup</code>, <code>adNetbiosName</code> and <code>staticIp</code> according to your needs.
<syntaxhighlight lang=nix>
{ config, lib, pkgs, ... }:
with lib;
let
  cfg = config.services.samba;
  samba = cfg.package;
  nssModulesPath = config.system.nssModules.path;
  adDomain = "samdom.example.com";
  adWorkgroup = "SAM";
  adNetbiosName = "SAMDOM";
  staticIp = "10.42.129.160";
in {
  # Disable resolveconf, we're using Samba internal DNS backend
  systemd.services.resolvconf.enable = false;
  environment.etc = {
    resolvconf = {
      text = ''
        search ${adDomain}
        nameserver ${staticIp}
      '';
    };
  };
  # Rebuild Samba with LDAP, MDNS and Domain Controller support
  nixpkgs.overlays = [ (self: super: {
    samba = (super.samba.override {
      enableLDAP = true;
      enableMDNS = true;
      enableDomainController = true;
      enableProfiling = true; # Optional for logging
      # Set pythonpath manually (bellow with overrideAttrs) as it is not set on 22.11 due to bug
    }).overrideAttrs (finalAttrs: previousAttrs: {
        pythonPath = with super; [ python3Packages.dnspython python3Packages.markdown tdb ldb talloc ];
      });
  })];
  # Disable default Samba `smbd` service, we will be using the `samba` server binary
  systemd.services.samba-smbd.enable = false; 
  systemd.services.samba = {
    description = "Samba Service Daemon";
    requiredBy = [ "samba.target" ];
    partOf = [ "samba.target" ];
    serviceConfig = {
      ExecStart = "${samba}/sbin/samba --foreground --no-process-group";
      ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
      LimitNOFILE = 16384;
      PIDFile = "/run/samba.pid";
      Type = "notify";
      NotifyAccess = "all"; #may not do anything...
    };
    unitConfig.RequiresMountsFor = "/var/lib/samba";
  };
  services.samba = {
    enable = true;
    enableNmbd = false;
    enableWinbindd = false;
    configText = ''
      # Global parameters
      [global]
          dns forwarder = ${staticIp}
          netbios name = ${adNetbiosName}
          realm = ${toUpper adDomain}
          server role = active directory domain controller
          workgroup = ${adWorkgroup}
          idmap_ldb:use rfc2307 = yes
      [sysvol]
          path = /var/lib/samba/sysvol
          read only = No
      [netlogon]
          path = /var/lib/samba/sysvol/${adDomain}/scripts
          read only = No
    '';
  }; 
}
</syntaxhighlight>
{{Evaluate}}
After evaluating, you should see that the Samba service crashed because we haven't setup the database yet.
To do that, let's run the following command, updated with your own configuration:
<code>
samba-tool domain provision --server-role=dc --use-rfc2307 --dns-backend=SAMBA_INTERNAL --realm=SAMDOM.EXAMPLE.COM --domain=SAMDOM --adminpass=Passw0rd
</code>
Then restart the samba service with <code>sudo systemctl restart samba</code>, and you're ready to go!


== Samba Client ==
== Samba Client ==
=== cifs mount ===
 
=== CIFS mount configuration ===


The following snippets shows how to mount a CIFS (Windows) share in NixOS.
The following snippets shows how to mount a CIFS (Windows) share in NixOS.
Line 9: Line 354:
<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
{
{
  # For mount.cifs, required unless domain name resolution is not needed.
  environment.systemPackages = [ pkgs.cifs-utils ];
   fileSystems."/mnt/share" = {
   fileSystems."/mnt/share" = {
      device = "//<IP_OR_HOST>/path/to/share";
    device = "//<IP_OR_HOST>/path/to/share";
      fsType = "cifs";
    fsType = "cifs";
      options = let
    options = let
        # this line prevents hanging on network split
      # this line prevents hanging on network split
        automount_opts = "x-systemd.automount,noauto,x-systemd.idle-timeout=60,x-systemd.device-timeout=5s,x-systemd.mount-timeout=5s";
      automount_opts = "x-systemd.automount,noauto,x-systemd.idle-timeout=60,x-systemd.device-timeout=5s,x-systemd.mount-timeout=5s";


      in ["${automount_opts},credentials=/etc/nixos/smb-secrets"];
    in ["${automount_opts},credentials=/etc/nixos/smb-secrets"];
   };
   };
}
}
Line 29: Line 376:
</syntaxhighlight>
</syntaxhighlight>


== Firewall ==
By default, CIFS shares are mounted as root. If mounting as user is desirable, `uid`, `gid` and usergroup arguments can be provided as part of the filesystem options:
<syntaxhighlight lang="nix">
{
  fileSystems."/mnt/share" = {
    # ... rest of the filesystem config omitted
    options = let
      automount_opts = "x-systemd.automount,noauto,x-systemd.idle-timeout=60,x-systemd.device-timeout=5s,x-systemd.mount-timeout=5s,user,users";
 
      in ["${automount_opts},credentials=/etc/nixos/smb-secrets,uid=1000,gid=100"];
    # or if you have specified `uid` and `gid` explicitly through NixOS configuration,
    # you can refer to them rather than hard-coding the values:
    # in ["${automount_opts},credentials=/etc/nixos/smb-secrets,uid=${toString config.users.users.<username>.uid},gid=${toString config.users.groups.<group>.gid}"];
  };
}
</syntaxhighlight>
 
=== Firewall configuration ===
 
Samba discovery of machines and shares may need the firewall to be tuned ([https://wiki.archlinux.org/index.php/Samba#.22Browsing.22_network_fails_with_.22Failed_to_retrieve_share_list_from_server.22 source]):
Samba discovery of machines and shares may need the firewall to be tuned ([https://wiki.archlinux.org/index.php/Samba#.22Browsing.22_network_fails_with_.22Failed_to_retrieve_share_list_from_server.22 source]):
in <code>/etc/nixos/configuration.nix</code>, add:
in <code>/etc/nixos/configuration.nix</code>, add:
<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
networking.firewall.extraCommands = ''iptables -t raw -A OUTPUT -p udp -m udp --dport 137 -j CT --helper netbios-ns";
networking.firewall.extraCommands = ''iptables -t raw -A OUTPUT -p udp -m udp --dport 137 -j CT --helper netbios-ns'';
</syntaxhighlight>
</syntaxhighlight>


== Browsing samba shares with GVFS ==
=== Command line ===
 
List shares
 
<pre>
smbclient --list localhost
</pre>
 
This should print
 
<pre>
$ smbclient --list localhost
Password for [WORKGROUP\user]:
 
Sharename      Type      Comment
---------      ----      -------
public          Disk     
IPC$            IPC      IPC Service (smbnix)
SMB1 disabled -- no workgroup available
</pre>
 
Mount as guest. <code>public</code> is your share name
 
<pre>
nix-shell -p cifs-utils
mkdir mnt
sudo mount.cifs -o sec=none //localhost/public mnt
</pre>
 
mount as user. <code>user</code> is your username
 
<pre>
sudo mount.cifs -o sec=ntlmssp,username=user //localhost/public mnt
</pre>
 
<code>sec=ntlmssp</code> should work.
for more values, see `man mount.cifs` (search for `sec=arg`)
 
=== Browsing samba shares with GVFS ===
 
Many GTK-based file managers like Nautilus, Thunar, and PCManFM can browse samba shares thanks to GVFS.
Many GTK-based file managers like Nautilus, Thunar, and PCManFM can browse samba shares thanks to GVFS.
GVFS is a dbus daemon which must be running for this to work.
GVFS is a dbus daemon which must be running for this to work.
Line 47: Line 450:


There are however some special cases.
There are however some special cases.
===== XFCE =====
===== XFCE =====
[[Xfce]] comes with a slimmed-down version of GVFS by default which comes with samba support compiled out. To have smb:// support in Thunar, we will use GNOME's full-featured version of GVFS:
[[Xfce]] comes with a slimmed-down version of GVFS by default which comes with samba support compiled out. To have smb:// support in Thunar, we will use GNOME's full-featured version of GVFS:
<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
   services.gvfs = {
   services.gvfs = {
     enable = true;
     enable = true;
     package = lib.mkForce pkgs.gnome3.gvfs;
     package = lib.mkForce pkgs.gnome.gvfs;
   };
   };
</syntaxhighlight>
</syntaxhighlight>
===== No desktop environment =====
===== No desktop environment =====
GVFS relies on polkit to gain privileges for some operations. Polkit needs an authentication agent to ask for credentials.
GVFS relies on polkit to gain privileges for some operations. Polkit needs an authentication agent to ask for credentials.
Desktop environments usually provide one but if you have no desktop environment, you may have to install one yourself:
Desktop environments usually provide one but if you have no desktop environment, you may have to install one yourself:
Line 64: Line 471:
</syntaxhighlight>
</syntaxhighlight>


===== DBUS =====


===== DBUS =====
Furthermore, if you happen to start your Window Manager directly, via <code>.xinitrc</code>, or directly invoke a Wayland compositor such as Sway, you should ensure that you launch dbus at startup in your session and export its environment. If you do not have a dbus session in your environment, you will see errors such as "Operation not supported" when attempting to browse the network.
Furthermore, if you happen to start your Window Manager directly, via <code>.xinitrc</code>, or directly invoke a Wayland compositor such as Sway, you should ensure that you launch dbus at startup in your session and export its environment. If you do not have a dbus session in your environment, you will see errors such as "Operation not supported" when attempting to browse the network.


Line 82: Line 489:
(Because <code>dbus-run-session</code> exits when the child process exits, it is only appropriate to use <code>dbus-run-session</code> with a process that will be running during the entire session. This is the case for Wayland compositors, but is not necessarily true for all configurations of X11 window managers.)
(Because <code>dbus-run-session</code> exits when the child process exits, it is only appropriate to use <code>dbus-run-session</code> with a process that will be running during the entire session. This is the case for Wayland compositors, but is not necessarily true for all configurations of X11 window managers.)


== Samba Server ==
== Troubleshooting ==
=== excerpt of /etc/nixos/configuration.nix ===
 
<syntaxhighlight lang="nix">
services.samba = {
  enable = true;
  securityType = "user";
  extraConfig = ''
    workgroup = WORKGROUP
    server string = smbnix
    netbios name = smbnix
    security = user
    #use sendfile = yes
    #max protocol = smb2
    hosts allow = 192.168.0  localhost
    hosts deny = 0.0.0.0/0
    guest account = nobody
    map to guest = bad user
  '';
  shares = {
    public = {
      path = "/mnt/Shares/Public";
      browseable = "yes";
      "read only" = "no";
      "guest ok" = "yes";
      "create mask" = "0644";
      "directory mask" = "0755";
      "force user" = "username";
      "force group" = "groupname";
    };
    private = {
      path = "/mnt/Shares/Private";
      browseable = "yes";
      "read only" = "no";
      "guest ok" = "no";
      "create mask" = "0644";
      "directory mask" = "0755";
      "force user" = "username";
      "force group" = "groupname";
    };
  };
};
</syntaxhighlight>


If your firewall is enabled, or if you consider enabling it:
=== Server log ===
<syntaxhighlight lang="nix">
networking.firewall.enable = true;
networking.firewall.allowPing = true;
networking.firewall.allowedTCPPorts = [ 445 139 ];
networking.firewall.allowedUDPPorts = [ 137 138 ];
</syntaxhighlight>


{{Evaluate}}
<pre>
sudo journalctl -u samba-smbd.service -f
</pre>


Samba should startup afterwards.
=== Stale file handle ===


If you plan to access a share as a user on the server, you'll need to run <code>smbpasswd -a <user></code> as root to set the password.
Trying to read the contents of a remote file leads to the following error message: "Stale file handle". If you have mounted a share via the method described in "cfis mount", adding the option <code>noserverino</code> might fix this problem. [https://askubuntu.com/questions/1265164/stale-file-handler-when-mounting-cifs-smb-network-drive-from-fritz-router]


=== stopping/restarting the services ===
=== NT_STATUS_INVALID_NETWORK_RESPONSE ===
<syntaxhighlight lang="console">
# systemctl stop samba
# systemctl start samba
# systemctl restart samba
</syntaxhighlight>


=== Use Cases ===
The error
==== Apple Time Machine ====
<code>protocol negotiation failed: NT_STATUS_INVALID_NETWORK_RESPONSE</code>
nixpkgs includes Samba4.8-git, which adds support for using shares for Time Machine backups on macOS 10.12+.
means "access denied".
Example configuration:
Probably you must fix your server's <code>hosts allow</code> section.
<syntaxhighlight lang="nix">
Note that <code>localhost</code> is the ipv6 localhost <code>::1</code>,
services.samba = {
and <code>127.0.0.1</code> is the ipv4 localhost
  package = pkgs.sambaMaster;
  shares = {
    tm_share = {
        path = "/mnt/Shares/tm_share";
        "valid users" = "username";
        public = "no";
        writeable = "yes";
        "force user" = "username";
        "fruit:aapl" = "yes";
        "fruit:time machine" = "yes";
        "vfs objects" = "catia fruit streams_xattr";
    };
  };
}
</syntaxhighlight>


==== Printer sharing ====
=== Permission denied ===


The `samba` packages comes without cups support compiled in, however `sambaFull` features printer sharing support.
Maybe check the <code>guest account</code> setting in your server config.
To use it set the `services.samba.package` option:
The default value is <code>nobody</code>,
but the user <code>nobody</code> has no access to <code>/home/user</code>:


<syntaxhighlight lang=nix>
<pre>
services.samba.package = pkgs.sambaFull;
$ sudo -u nobody ls /home/user
</syntaxhighlight>
[sudo] password for user:
ls: cannot open directory '/home/user': Permission denied
</pre>


A printer share that allows all members in the local network printing could look like this:
As workaround, set <code>guest account = user</code>,
where <code>user</code> is your username


<syntaxhighlight lang=nix>
== See also ==
{ pkgs, ... }: {
  services.samba = {
    enable = true;
    package = pkgs.sambaFull;
    extraConfig = ''
      load printers = yes
      printing = cups
      printcap name = cups
    '';
    shares = {
      printers = {
        comment = "All Printers";
        path = "/var/spool/samba";
        public = "yes";
        browseable = "yes";
        # to allow user 'guest account' to print.
        "guest ok" = "yes";
        writable = "no";
        printable = "yes";
        "create mode" = 0700;
      };
  };
  systemd.tmpfiles.rules = [
    "d /var/spool/samba 1777 root root -"
  ];
}
</syntaxhighlight>


== links ==
* [https://search.nixos.org/options?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=services.samba Samba Options in NixOS on unstable]
* [https://search.nixos.org/options/?query=services.samba Samba Options in NixOS]
* [https://wiki.archlinux.org/title/Samba Samba in the Arch Linux Wiki]
* [https://www.samba.org/samba/docs/current/man-html/smb.conf.5.html smb.conf man page]


[[Category:Services]]
[[Category:Server]]
[[Category:Applications]]