本文简单说明如何在 ubuntu 16.04 安装配置 samba 服务, 实现与 windows 共享文件.

安装:

sudo aptitude install samba

编辑 /etc/samba/smb.conf 配置。

参考:

samba 配置项可以由多个空格分割的单词组成, 这个要注意一下, 如:

create mask = 0755

重要的几个配置有 security, read only, create mask, directory mask 几个。 多数配置都有合适的默认值, 简单做如下修改即可。

  1. read only 默认是 yes, 要创建可写共享, 可修改为 no
  2. create mask 默认是 0755, 一般文件不需要执行权限, 建议修改为 0644
  3. 默认禁止访问共享目录之外的软链接(为安全考虑),个人电脑可取消这个限制,方便使用软链接设置共享目录和文件。

    全局配置区添加如下配置即可:

    wide links = yes
    allow insecure wide links = yes
  4. 默认注释了 [homes] 配置, 可取消注释, 允许访问用户 home 目录。

    [homes]
    ;   comment = Home Directories
    ;   browseable = no
  5. 默认配置了打印机共享, 通常不需要, 可注释掉这部分内容。

    #[printers]
    #   comment = All Printers
    #   browseable = no
    #   path = /var/spool/samba
    #   printable = yes
    #   guest ok = no
    #   read only = yes
    #   create mask = 0700
    #
    ## Windows clients look for this share name as a source of downloadable
    ## printer drivers
    #[print$]
    #   comment = Printer Drivers
    #   path = /var/lib/samba/printers
    #   browseable = yes
    #   read only = yes
    #   guest ok = no
  6. 添加自定义共享配置

    [repo]
        path = /repo

如果修改了配置, 重启 samba 服务即可生效:

sudo systemctl restart smbd

samba 有自己的用户认证方式, 并与 linux 用户做映射实现权限控制。 默认同一用户名即同一用户。

可使用 smbpasswd 命令添加 samba 用户并设置密码:

sudo smbpasswd -a $USER

如果安装了 libpam-smbpass, 则 linux 用户登录时会自动同步用户名和密码到 samba 服务。 但 ubuntu 16.04 下貌似没这个软件包了, 那就手动使用 smbpasswd 命令操作一下吧。

参考:

samba 在 Linux 文件系统上提供 windows 网络文件系统(CIFS)(而不是专门提供 CIFS),从而实现 windows 可以共享访问 Linux 文件系统。 所以这套文件系统有两个视角,一个是从 windows 的角度看,一个是从 Linux 的角度看。

文件系统不只是读写数据,还跟用户认证(谁在读写操作)和权限控制(操作者有哪些权限)紧密相连。 而 samba 需要想办法把两者联系对应起来,就像 cygwin 也要做类似的映射(参考: https://cygwin.com/cygwin-ug-net/ntsec.html)。

搭建/使用这套文件系统有以下事项需要关注:

  1. 用户认证。使用用户名密码登录,samba 使用用户名将登录用户和 Linux 用户关联起来。

  2. 文件大小写。windows 不区分大小写,Linux 区分大小写。samba 默认配置是保留大小写但不区分大小写。 所以如果一个文件夹下有两个名字相同大小写不同的文件夹,Linux 访问正常,windows 访问就会混乱,应避免这种用法。

  3. 文件属性和权限。dos/windows 下有 R/S/H 等属性(参考 attrib 命令),Linux 下有 user/group/other rwx 标志位。 samba 默认将一些属性(如 readonly)和标志位映射起来,没有映射的就不支持。 samba 也支持配置将这些属性存储到 Linux 文件系统的 user_xattr,默认没有打开。

cygwin 安装在 samba 共享目录的问题

默认没有映射 System 属性(参考 man smb.confmap system 一节), 而 Cygwin 默认 symlink 依赖此属性(参考 https://cygwin.com/cygwin-ug-net/using.html#pathnames-symlinks ), 导致 samba 共享上的 Cygwin 不能正常工作。 解决办法是打开映射或使用 user_xattr 存储 dos 属性,xfs 默认开启了 user_xattr,ext4 需要 mount 时手动指定开启。 但还有用户和组映射的问题,安装 cygwin 尝试执行 /bin/rebase-trigger 脚本时报没有权限。

windows 用户是 "域\名字" 两层结构,没有加入域的计算机本地用户域为本机(一台计算机即一个本机域?),而 Linux 用户用 id 标识。 与 samba 关联的 Linux 用户默认显示为本机域下面的本地用户,没有关联 samba 的用户和组显示为 "Unix User" 和 "Unix Group" (两个特殊域?) 下的用户和组。 windows 用户也有一个 SID,但这个 SID 需要与域关联,只有在关联域下才有意义(?)。 共享文件的用户和组只能设置为共享文件所在域(默认即 samba 主机)下的用户和组。 Linux 文件系统有所有者和所有组,windows 只有所有者,没有所有组,然后可以设置各个用户和组的权限,特殊公共用户 Everyone 表示所有人。

参考 cygwin 做用户映射的逻辑: https://cygwin.com/cygwin-ug-net/ntsec.html#ntsec-mapping , 可以映射通用用户,本机本地用户,本机所在主域用户,但无法映射其他机器上的本地用户(无法协调 windows SID 和 Linux uid/gid 的映射关系)。 如 F: 是 samba 网络盘,文件所有者为 samba 主机上本地用户, 网络盘上运行的 cygwin 无法识别网络文件系统(整个 cygwin 根文件系统)所属用户和组, 显示为 "Unknown+User","Unix_Group+1000",不能与本地用户(即 cygwin 运行用户) 匹配。 但本地文件系统(如 "/d/cygwin64/" 可以识别)。

C:\Users\hanyong>F:\cygwin64\bin\ls -ald / /d/ /f/ /d/cygwin64/
drwxrwxrwx+ 1 Unknown+User Unix_Group+1000 0 Oct 18 00:14 /
drwx------+ 1 Unknown+User SYSTEM          0 Oct 18 01:28 /d/
drwxrwxrwx+ 1 hanyong      None            0 Jun 23 22:19 /d/cygwin64/
drwxr-xr-x  1 Unknown+User Unix_Group+1000 0 Oct 18 09:20 /f/

C:\Users\hanyong>F:\cygwin64\bin\ls -ald / /d/ /f/ /d/cygwin64/ -n
drwxrwxrwx+ 1 4294967295 4278191080 0 Oct 18 00:14 /
drwx------+ 1 4294967295         18 0 Oct 18 01:28 /d/
drwxrwxrwx+ 1     197610     197121 0 Jun 23 22:19 /d/cygwin64/
drwxr-xr-x  1 4294967295 4278191080 0 Oct 18 09:20 /f/

C:\Users\hanyong>F:\cygwin64\bin\id
uid=197610(hanyong) gid=197121(None) groups=197121(None),545(Users),4(INTERACTIVE),66049(CONSOLE LOGON),11(Authenticated Users),15(This Organization),113(本地帐户),66048(LOCAL),262154(NTLM Authentication),4294967295(Unknown+Group),401408(Medium Mandatory Level)

但发现本地文件系统上运行的 cygwin 可以识别挂载的 samba 共享文件所有者为本地用户(为什么?)。

C:\Users\hanyong>D:\cygwin64\bin\ls -ald / /d/ /f/ /d/cygwin64/
drwxrwxrwx+ 1 hanyong None 0 Jun 23 22:19 /
drwxr-xr-x  1 hanyong None 0 Oct 18 01:28 /d/
drwxr-xr-x  1 hanyong None 0 Jun 23 22:19 /d/cygwin64/
drwxr-xr-x  1 hanyong None 0 Oct 18 09:20 /f/

C:\Users\hanyong>D:\cygwin64\bin\ls -ald / /d/ /f/ /d/cygwin64/ -n
drwxrwxrwx+ 1 197610 197121 0 Jun 23 22:19 /
drwxr-xr-x  1 197610 197121 0 Oct 18 01:28 /d/
drwxr-xr-x  1 197610 197121 0 Jun 23 22:19 /d/cygwin64/
drwxr-xr-x  1 197610 197121 0 Oct 18 09:20 /f/

C:\Users\hanyong>D:\cygwin64\bin\id
uid=197610(hanyong) gid=197121(None) groups=197121(None),545(Users),4(INTERACTIVE),66049(CONSOLE LOGON),11(Authenticated Users),15(This Organization),113(本地帐户),66048(LOCAL),262154(NTLM Authentication),4294967295(Unknown+Group),401408(Medium Mandatory Level)

推测 windows 读写操作权限控制的原理是,虽然当前进程是本机用户,但 samba 共享使用远程机器上的用户登录, 所以读写操作使用远程机器上的用户进行,没有问题。 而 cygwin 发现当前进程用户(本地用户)与文件系统用户不匹配,一些操作权限验证即不通过。 如安装 cygwin 时执行 rebase-trigger 脚本报错,但执行二进制文件却可以,执行二进制文件不校验 execute 标识位? 推测二进制文件直接调 windows API 运行,windows 检查可执行位 OK (以远程机器用户访问检查)。 而 windows API 不能直接运行脚本,先由 cygwin 处理,而 cygwin 检查可执行位不通过(以当前用户(作为other)检查)。

C:\Users\hanyong>F:\cygwin64\bin\env /bin/ls /bin/ls /bin/rebase-trigger -al
-rwxrw-r-- 1 Unknown+User Unix_Group+1000 127507 Feb  4  2017 /bin/ls
-rwxrw-r-- 1 Unknown+User Unix_Group+1000   1476 Feb  7  2016 /bin/rebase-trigger

C:\Users\hanyong>F:\cygwin64\bin\env /bin/rebase-trigger
/usr/bin/env: '/bin/rebase-trigger': Permission denied

另外按默认配置, windows 上修改 samba 共享的所有者和权限貌似不会生效(确认?)。

要让 cygwin 正确识别和使用 samba 共享的权限控制,需要把 3 者的用户和权限打通联系起来。 对应 cygwin (Unix like) -> samba/cifs (Windows) -> Linux (Unix like)。

可能的解决办法:

  1. 加入域,本机进程和 samba 共享都使用域用户。依赖重, windows 10 家庭版不能加入域。
  2. samba 共享使用 Everyone 权限, 弱化权限控制。
  3. samba 共享文件系统用户映射成本地用户。是否可行?

尝试方法2, 发现 Everyone 是 windows 下的一个虚拟用户,samba 没有这个用户,没法设置文件所有者为 Everyone。 就连 cygwin 下的 getent passwdgetent group 都没有 Everyone,可见这是个完全虚拟的用户。 想过指定一个 Linux 用户为 Everyone,没找到 samba 有自定义 SID/uid map 的地方, 只发现 idmap 保存, winbindd 查询,没有自定义添加条目的地方,好像都是 samba 添加用户时自动生成 SID 并保存管理(?)。 查看 man idmap_tdb2 倒是发现可以指定一个脚本查询 SID/uid 映射关系, 但他要你处理所有 id 映射,而不是只加一个自定义映射。 而且这是个全局配置,不能只操作一个共享目录,好在 idmap 配置可以限定 uid 范围。 作为 workaround 可以配置一个 idmap 只包含一个 uid,设置脚本将这个 idmap 对应的 SID 无条件设置为 Everyone 的 SID (实际只有一个uid)。 但是这样还有个问题,samba 共享文件的权限会自动根据 other 权限生成 Everyone 的权限条目, 如果 Everyone 同时是 user (和 group?),那么 Everyone 就会出现多个权限条目,这会不会有问题? 另外用一台 windows 机器共享 Everyone 为所有者测试,共享上的 cygwin 还是不能正常工作,这条路不通。 方法3也没想到可能的办法,那么就只剩下方法1,加入域。