Sftp chroot howto

我之前在服务器上应用 Ftp chroot,那设置起来相对简单。但 Ftp service 不是服务器预装的,通常我会装 proftpd 或 vsftpd。

这次我想为什么不用服务器预装的 Sftp service?我有点钻了牛角尖—— Sftp 比 Ftp 好在哪?更安全?似乎这增加的安全性对我来说毫无意义。少占点硬盘空间?空余字节多着呢。总之,我想到了就想把它用起来。

首先新建一个用户组,sftponly,只要是这个组的用户 Sftp 登录后都会被 chroot。


groupadd sftponly

然后把需要 chroot 的用户都加到 sftponly 组


usermod -a -G sftponly user1

如果是新用户,则在创建的时候直接参组。


useradd -U -G sftponly -s /sbin/nologin -d /path/to/user1_home user1

然后要保证 /path/to/user1_home 这个目录及所有各级父目录的属主是 root,属性 755。user1 在他的 home directory 下是没有写权限的,不能由他自己创建文件或文件夹。这也容易理解,因为他被 chroot 以后,他的 home directory 看上去是系统根目录,普通用户当然不能在根目录这一级有写权限。这时需要 root 事先为 user1 创建好若干文件夹,如 /path/to/user1_home/documents、/path/to/user1_home/pictures,并将 documents、pictures 的属主指定为 user1。

然后我按网上的指点依样画瓢,在 /etc/ssh/sshd_config 里添加了这几行:


Match Group sftponly
    ForceCommand internal-sftp
    ChrootDirectory %h

然后重启 sshd service


service sshd restart

理论上这时 user1 已经可以在 Sftp 登录是被 chroot,限定在他的 home directory。但是 user1 怎么都登录不了,碰到的错误是:

Connection closed by server with exitcode 1

原来,在我的 /etc/ssh/sshd_config 里有这么一行


Subsystem       sftp    /usr/libexec/openssh/sftp-server

这是服务器预装时的配置,可能不是所有的服务器都这样,但有一定的普遍性。

这说明,Sftp 子系统是由 /usr/libexec/openssh/sftp-server 支持的。而在被 chroot 的 user1 看来,是没有 /usr/libexec/openssh/sftp-server 这个文件的。怎么办?两个办法:

办法一:将那一行改为


Subsystem     sftp    internal-sftp

办法二:在 user1 的 home directory 下建一个副本 %h/usr/libexec/openssh/sftp-server,只是简单的文件拷贝。

 

Manage gallery image with customised URL in Magento

Magento 后台对产品图片的管理操作非常简单,直接在浏览器里从本地上传,然后指定哪个作大图(image)、哪个作小图(small image)、哪个作缩略图(thumbnail)。这种傻瓜式的操作有三大缺点:

  • 不适合大批量图片的管理;
  • 图片上传后图片的存放位置乱序,不方便远程文件管理。(abc.jpg 上传后被存放在 /media/catalog/product/a/b/abc.jpg。如果之前已经有同名文件,新上传的文件自动更名为abc_1.jpg, abc_2.jpg,以此类推。)
  • 搜索引擎会从图片的 URL 里获取图片的部分信息,杂乱的 URL 不利于图片 SEO。

我认为最理想的图片管理模式是:在本地按产品分类分级维护一个图片库,用 FTP 上传到服务器,在 Magento 后台可以浏览这些文件(对 Magento 来说是本地文件),然后为某个产品选定它的大图、小图和缩略图。这样 Magento 里保存的图片位置信息就保持了自定义的产品分类信息。

Magento 的自动缩放图功能很好用,但自动缩放图生成的文件 URL 又臭又长,肯定不利于 SEO,而且服务器硬盘上留下一大堆乱序规则生成的文件夹,实在难看(有种屋子没打扫的感觉)。鉴于一个优质的电子商务网站本应该对整站的图片大小有统一的规范,不妨在本地制作好小图和缩略图,不依赖 Magento 的自动缩放图功能。

好多年前我就想做个 Magento module 来优化 Magento 的图片管理,但事务繁忙,也不知道什么时候能静下心写代码。与其让听众苦等我的 module,不如我介绍一下 Magento 数据库中 gallery 的结构,让大家懂得直接操作数据库去搭 product 和 local images 之间的桥。

首先做两个准备工作。

一是查好每个产品的 ID 备用。如果人可以 SKU 识别产品,那就准备一张 SKU – ID 的一一对应表;如果人可以 product name 识别产品,那就准备一张 product name – ID 的一一对应表。

二是把每个产品的大图、小图和缩略图命名得有意义,比如是大图是 product-name-1.jpg, product-name-2.jpg, product-name-3.jpg,小图是 product-name-s.jpg,缩略图是 product-name-t.jpg(因为 small 和 thumbnail 不是产品的关键字,所以没必要拼写完整,用自己人能看明白的代号就可以)。FTP 上传图片至 /media/catalog/product/category-name/sub-category-name/SKU/,一个产品的图片归在一个文件夹下。

现在开始正式操作数据库。操作涉及到 4 个产品属性(attribute)和 4 张表(table)。

4 个产品属性:image, small_image, thumbnail, media_gallery.

4 张表:magento_eav_attribute, magento_catalog_product_entity_media_gallery, magento_catalog_product_entity_media_gallery_value, magento_catalog_product_entity_varchar.

第一步:在 magento_eav_attribute 中查出 4 个产品属性的 attribute_id。在我碰到的 Magento 早期版本中,4 个产品属性的 attribute_id 分别是:

  • image: 70
  • small_image: 71
  • thumbnail: 72
  • media_gallery: 73

在最新的 1.7.0.0 中,4 个产品属性的 attribute_id 分别是:

  • image: 85
  • small_image: 86
  • thumbnail: 87
  • media_gallery: 88

当然可以顺便看一下 product 的 entity_type_id,不出意外的话,这应该是 4。后面会用到。

第二步:在 magento_catalog_product_entity_media_gallery 插入记录。一张图片就是一条记录,插入记录就是定义产品和图片之间一对多的关系。

Insert record to magento_catalog_product_entity_media_gallery
Insert record to magento_catalog_product_entity_media_gallery

magento_catalog_product_entity_media_gallery 中各 column 的意义是:

  • value_id:记录 ID,可以留空让数据库自动生成。
  • attribute_id:media_gallery 的 attribute_id。
  • entity_id:产品 ID。
  • value:文件存放位置信息(略去 /media/catalog/product 部分)。
Gallery records for one product
Gallery records for one product

做完这两步就可以在 Magento 后台 Manage Products 的 Images 那一页上看到属于这产品的图片。后面几步可以移至 Magento 后台完成。我继续介绍如何直接操作数据库,是让大家知道如何用数据库的导入功能去批量处理。

第三步:在 magento_catalog_product_entity_media_gallery_value 插入记录。这等效于在 Magento 后台为每个图片在各个商店设定 Label, Sort Order, Exclude 值。

如果只有一个商店,一条 magento_catalog_product_entity_media_gallery 记录就对应一条 magento_catalog_product_entity_media_gallery_value 记录。

如果有多个商店,default store_id 就是 0,先按一条 magento_catalog_product_entity_media_gallery 记录对应一条 magento_catalog_product_entity_media_gallery_value 插入记录。假设另有两个商店,store_id 分别是 1 和 2,store_id 1 沿用 store_id 0 的 default 值,store_id 2 则使用一组不同的 label/position/disable 值。这样,不需要为 store_id 1 多插入一条记录,因为 Magento 的 Website/Storegroup/Storeview 的规则是没有额外记录就是使用 default value。只需要为 store_id 2 多插入一条记录,这条记录优先于 default value,但只为 store_id 2 而生效。

magento_catalog_product_entity_media_gallery_value 中各 column 的意义是:

  • value_id:匹配 magento_catalog_product_entity_media_gallery 的记录 ID。
  • store_id:商店 ID。
  • label:图片说明。
  • position:图片排序。
  • disabled:0 就是不 Exclude,1 就是 Exclude。
Insert record to magento_catalog_product_entity_media_gallery_value
Insert record to magento_catalog_product_entity_media_gallery_value
Disable (Exclude) small image and thumbnail
Disable (Exclude) small image and thumbnail

第四步:在 magento_catalog_product_entity_varchar 插入记录。这等效于在 Magento 后台为每个产品在各个商店设定哪个是默认大图(大图可以有多张,只有一张是默认的)、哪个是小图、哪个是缩略图。

magento_catalog_product_entity_varchar 中各 column 的意义是:

  • value_id:记录 ID,可以留空让数据库自动生成。
  • entity_type_id:不出意外的话,应该填 4。
  • attribute_id:image/small_image/thumbnail 的 attribute_id。
  • store_id:商店 ID。
  • entity_id:产品 ID。
  • value:文件存放位置信息(略去 /media/catalog/product 部分)
Insert record to magento_catalog_product_entity_varchar
Insert record to magento_catalog_product_entity_varchar

假设以前用 Magento 后台上传过产品图片,删除了,保存产品,这时,服务器硬盘上的图片不会随之删除,数据库里的 image/small_image/thumbnail 记录也不会随之删除(只是 value column 的值被 NULL 代替)。这也是我不喜欢用 Magento 后台来管理产品图片的一个原因。

Duplicate record error
Duplicate record error

这些 NULL 值的记录会导致插入不成功,因为 magento_catalog_product_entity_vartype 有规定 entity_id, attribute_id, store_id 三值组的唯一性。不让插的话编辑原记录。

Search by attribute_id and entity_id
Search by attribute_id and entity_id
Search result by attribute_id and entity_id
Search result by attribute_id and entity_id

或者,把原有的 image/small_image/thumbnail 记录全删了,再插。

Search by attribute_id
Search by attribute_id
Search result by attribute_id
Search result by attribute_id

这四步做完后,Magento 前后台就显示了指定的图片。

Magento GUI manage products images
Magento GUI manage products images
Magento store front product page
Magento store front product page

这时 Magento 的自动缩放图功能仍在生效,需要修改模板让 Magento 直接使用指定的大图、小图和缩略图,这里不再多述。

Ftp user chroot setting on CentOS

在 CentOS 上,怎么把 linux user ftp 登录后限制在它的 home directory?很久以前我设置过一次,知道是很简单的一个设置,今天需要同样的事情再做一次,竟然怎么也想不起来。要命的是,google 了好几个小时都不得要领。

可以说半天时间是浪费掉的。最后当然是重新找到了办法,赶紧记下来:

Edit /etc/proftpd.conf, add

DefaultRoot ~

当然前提预装有 proftpd。如果还预装 plesk,则更简单,/etc/proftpd.conf 已含有

DefaultRoot ~ psacln

只要把新建的用户归入 secondary group psacln 即可。

Create FTP users on Godaddy free products

前文讲到 Godaddy 随域名附送的免费空间非常强大,不嫌广告烦的话,做个个人网站绝对没有问题。

既然可以建网站,就可以作文件服务器,支持 FTP,能自助创建多达50个 FTP user。

Create FTP user in Godaddy hosting account
Create FTP user in Godaddy hosting account

Godaddy hosting account username / password 本身也是 FTP account。如果是合作伙伴要求上传或下载文件,当然不可以把自己的 username / password 告诉别人,应该分别为他们创建单独的帐号。Godaddy 需要十几分钟完成帐号同步,所以要等帐号状态从 Pending Setup 到 Setup 才能用。

Mount FTP file system on CentOS

有了 SFTP 以后,FTP 几乎没有用武之地了,研究 FTP 大多是因为某些软件或服务器只支持 FTP。

1&1 独立主机附赠一个跟硬盘容量相等的 FTP 备份空间,此 FTP 只能由 root server 访问,但 1&1 并没有提供现成的备份软件(1&1 营销上的失误)。FTP 备份空间对我来说用处不大,但 250 G 的空间空着也是浪费,于是我自创了我的备份方案,此中关键点是如何在 CentOS 上加载 FTP 文件系统。

经一番搜索,找到 curlftpfs,用它可以加载 FTP 文件系统,但 Fedora 可以直接 yum install curlftpfs,CentOS 里没有,得用 DAG repository,所以第一步:安装 DAG repository。

rpm -Uhv http://apt.sw.be/redhat/el5/en/x86_64/rpmforge/RPMS//rpmforge-release-0.3.6-1.el5.rf.x86_64.rpm

第二步:安装 curlftpfs

yum install curlftpfs

第三步:没有第三步,已经可以使用 curlftps 了。如果要加载 FTP 文件系统

curlftpfs ftp://ftp_host/sub_dir mount_point -o user="ftp_username:ftp_password", uid=user_id, gid=group_id, allow_other

如果在启动时自动加载 FTP 文件系统,在 /etc/fstab 中添加一行

curlftpfs#ftp_uername:ftp_password@ftp_host/sub_dir mount_point fuse rw,uid=user_id, gid=group_id,user,allow_other 0 0
(很多关于 curlftpfs in fstab 的介绍使用了 noauto,这样的话,还需执行 mount mount_point 才能真正加载)

卸载 FTP 文件系统

fusermount -u mount_point