Samba Server
Samba is a free software re-implementation of the SMB networking protocol.
This integration provides a wrapper around the upstream nixpkgs module to create and manage
a Samba server declaratively, providing some easy to configure features such as:
- define default options to set on all shares
- define shares declaratively
- configure and provision users with samba credentials
- integration to emulate use of openFirewall options in upstream nixpkgs module, but on a per interface + source IP basis
Example Configurations
Samba Module Options Reference
Basic Public Share
This shows a simple Samba server with a single public share.
provision.fs.samba.server = {
  enable = true;
  firewall.enable = true; # open firewall for interfaces defined in `interfaces1
  interfaces = {
    localhost.subnet = "lo";
    eth0.subnet = "eth0"; # add external ethernet devices
  };
  global = {
    workgroup = "WORKGROUP";
    "bind interfaces only" = "yes";
    "server string" = "Samba %v on (%L)";
    "netbios name" = "SMBNIX";
    "security" = "user";
  };
  shares.public = {
    path = "/pool/public";
    browseable = true;
    read.only = false;
    guest.ok = true;
    create.mask = "0644";
    directory.mask = "0755";
    # force user permissions to a user we create inline below, you can set your own existing user in `users.users` instead
    force.user = "smb-public";
    force.group = "users";
  };
  # you can optionally generate a user in `users.users` inline here
  users.smb-public = {
    uid = 7991; # only required if user doesn't already exist in `users.users`
    configureUser = true; # only required if user doesn't already exist in `users.users`
    group.name = "users"; # optional
  };
};
You can then connect to the server by running
# on samba server
nix shell nixpkgs#cifs-utils
mount.cifs //localhost/public /mnt -o guest
# optionally force the local user/group to a local user on the guest mount
mount.cifs //localhost/public /mnt -o guest,uid=1000,gid=100
# or with username/group
mount.cifs //localhost/public /mnt -o guest,uid=myuser,gid=users
# on another machine accessible via network on eth0
mount.cifs //<samba-ip>/public /mnt -o guest
User Authentication + Generated Passwords
You can also configure samba users, and their access to files.
You can also optionally provision samba users automatically by setting provisionSamba to true and sambaPasswordFile to a location
that is readable on the host (like /root/samba-password) or provisioned by a secrets management framework
like agenix and setting the value to config.agenix.secrets.samba-password.path.
If you don’t use this option, then you will need to create your own samba users for users in valid.users with:
# on the samba server
smbpasswd -a smb-user
Example Configuration
users.users.media = {
  uid = 2000;
  group = "media";
};
users.grousp.media.gid = 2000;
provision.fs.samba.server = {
  enable = true;
  firewall.enable = true; # open firewall for interfaces defined in `interfaces1
  interfaces = {
    localhost.subnet = "lo";
    eth0.subnet = "eth0"; # add external ethernet devices
  };
  global = {
    workgroup = "WORKGROUP";
    "bind interfaces only" = "yes";
    "server string" = "Samba %v on (%L)";
    "netbios name" = "SMBNIX";
    "security" = "user";
  };
  shares.media = {
    path = "/media";
    browseable = true;
    read.only = false;
    create.mask = "0644";
    directory.mask = "0755";
    # force user permissions to a user we create inline below, you can set your own existing user in `users.users` instead
    force.user = "media";
    force.group = "media";
    valid.users = [ "smb-media" ];
  };
  users.smb-media = {
    uid = 7991; # only required if user doesn't already exist in `users.users`
    configureUser = true; # only required if user doesn't already exist in `users.users`
    group.name = "users"; # optional
    # choose to automatically provision samba
    provisionSamba = true;
    sambaPasswordFile = "/root/smb-media-password";
  };
};
You can then connect to the server by running
# on samba server
nix shell nixpkgs#cifs-utils
mount.cifs //localhost/media /mnt -o user=media,password=mypassword
Define Shared Options
This is configuration equivalent with the above Basic Public Share configuration
provision.fs.samba.server = {
  enable = true;
  firewall.enable = true; # open firewall for interfaces defined in `interfaces1
  interfaces = {
    localhost.subnet = "lo";
    eth0.subnet = "eth0"; # add external ethernet devices
  };
  global = {
    workgroup = "WORKGROUP";
    "bind interfaces only" = "yes";
    "server string" = "Samba %v on (%L)";
    "netbios name" = "SMBNIX";
    "security" = "user";
  };
  default.opts = {
    browseable = true;
    read.only = false;
    guest.ok = true;
    create.mask = "0644";
    directory.mask = "0755";
    # force user permissions to a user we create inline below, you can set your own existing user in `users.users` instead
    force.user = "smb-public";
    force.group = "users";
    hosts.allow = [ "127.0.0.1" "localhost" ];
  };
  shares.public.path = "/pool/public";
  # optionally override the default options from `default.opts`
  shares.public.force.user = "media";
  shares.public.force.group = "media";
  # NOTE: changing array values like `hosts.allow` doesn't merge the lists, but overrides
  shares.public.hosts.allow = [ "10.98.1.0/24" ]; # this would not allow localhost access from the samba server itself
  # you can optionally generate a user in `users.users` inline here
  users.smb-public = {
    uid = 7991; # only required if user doesn't already exist in `users.users`
    configureUser = true; # only required if user doesn't already exist in `users.users`
    group.name = "users"; # optional
  };
};
Samba with Wireguard
I had some issues getting the Samba daemons to listen on wireguard interfaces when using the global bind interfaces only = yes setting.
Apparently this is due to Wireguard not supporting broadcast and not automatically listening on these interfaces.
You can get around this issue without setting bind interfaces only = no (so listening on all interfaces) by setting the global interfaces
to include the subnet of your wireguard network, I tend to use the exact wireguard IP for the samba server like 10.8.0.7/24 and it works
well.
A shorthand is provided via the interfaces.<interface-name>.subnet configuration which sets these values for you.
provision.fs.samba.server = {
  enable = true;
  firewall.enable = true; # open firewall for interfaces defined in `interfaces1
  interfaces = {
    localhost.subnet = "lo";
    eth0.subnet = "eth0"; # add external ethernet devices
    # use the exact wireguard interface name and set the subnet to the samba server's wireguard IP (with mask)
    vpn.subnet = "10.8.0.7/24";
  };
  default.opts = {
    # allow VPN subnet by default to shares
    hosts.allow = [ "10.8.0.0/24" "127.0.0.1" "localhost" ];
    # you can also whitelist specific IPs only if you wish
    # hosts.allow = [ "10.8.0.91" "10.8.0.103" "127.0.0.1" "localhost" ];
  };
};
Test Configuration
The server + client integrations are tested in tests/samba/basic.nix..
Troubleshooting
The below is a miscellaneous list of tools you can use to debug issues, or comments on configurations:
- Use 
testparmon the Samba server to show config warnings. smbclient -L localhost --user=smb-userto list sharessmbstatusto list current active connections- WARNING: setting guest account to an existing user can create numerous weird permission issues that are hard to debug