apache2 httpd
three-pass access control system
Allow,Deny,Order三个指令的组合由modaccesscompat模块提供,在2.4版本后被设置为过时,由modauthzhost取代。modauthzhost提供的指令为require。
三阶段访问控制要处理所有Allow和Deny指令,而传统防火墙配置一般只应用第一条匹配的规则。三阶段访问控制应用最后匹配的规则。Order,Allow,Deny三指令在配置文件中的次序是没有关系的,Order指令的值决定了处理的次序。
Order的值主要有两种:
- Allow,Deny
- 处理Allow指令,至少匹配一条Allow指令,否则会被拒绝。
- 处理Deny指令,若匹配,则拒绝。
- 没有任何匹配,则拒绝。
- Deny,Allow
- 处理Deny指令,如有匹配,并且没有Allow指令匹配,则拒绝。
- 没有任何匹配,则允许。
- Caching must be enabled for this URL. See the CacheEnable and
- The response must have a HTTP status code of 200, 203, 300, 301 or
- The request must be a HTTP GET request.
- If the response contains an "Authorization:" header, it must also
- If the URL included a query string (e.g. from a HTML form GET
- If the response has a status of 200 (OK), the response must also
- If the response includes the "private" option in a "Cache-Control:"
- Likewise, if the response includes the "no-store" option in a
- A response will not be stored if it includes a "Vary:" header
- Prefork Apache预先创建多个子进程。
- Perchild Prefork的变种,允许为子进程设置权限。
- Threadpool
- Worker Prefork和Threadpool的混合体。每个子进程支持多线程。
original和final request
CustomLog指令的说明中指出: By default, the % directives %s, %U, %T, %D,
and %r look at the original request while all others look at the final
request. So for example, %\>s can be used to record the final status of
the request and %\
user on a request that is internally redirected to an unauthenticated
resource. request到达apache后,可能会被更改,因此有original和final之分。
modcache
What Can be Cached?
The full definition of which responses can be cached by an HTTP cache is
defined in RFC2616 Section 13.4 Response Cacheability, and can be summed
up as follows:
CacheDisable directives.
410.
contain an "s-maxage", "must-revalidate" or "public" option in the
"Cache-Control:" header, or it won't be cached.
method) it will not be cached unless the response specifies an
explicit expiration by including an "Expires:" header or the max-age
or s-maxage directive of the "Cache-Control:" header, as per RFC2616
sections 13.9 and 13.2.1.
include at least one of the "Etag", "Last-Modified" or the "Expires"
headers, or the max-age or s-maxage directive of the
"Cache-Control:" header, unless the CacheIgnoreNoLastMod directive
has been used to require otherwise.
header, it will not be stored unless the CacheStorePrivate has been
used to require otherwise.
"Cache-Control:" header, it will not be stored unless the
CacheStoreNoStore has been used.
containing the match-all "\*".
参考: <http://httpd.apache.org/docs/2.4/en/caching.html>
MPM(Multi-Processing Module)
Unix系统中可用的MPM有四种:
多线程模块,由于很多时候无法保证线程安全,此模块大多数情况下不推荐使用。
很多Apache和PHP用到的库都不是线程安全的,因此PHP环境下基本使用Prefork
MPM。
配置动态Filter
apache2的官方文档中说AddOutputFilterByType已经过时,使用时可能会有问题,而在许多参考文献中还是大量使用,Ubuntu
10.4默认情况下也使用该指令。然而在把该指令用于Moodle时确实出现问题。下面的配置是典型的用法:
`` example
AddOutputFilterByType DEFLATE text/html text/plain text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE text/javascript application/javascript application/x-javascript application/ecmascript
AddOutputFilterByType DEFLATE application/rss+xml
实践中发现图片(image/gif)等也被用gzip压缩,结果不是容量变小,而是容量变得更大。起先我以为是PHP中zlib.output<sub>compression参数问题导致</sub>,反复修改,问题依旧,最后到httpd.conf中把上面的语句注释掉,图片才恢复正常。最终使用如下配置:
example
FilterDeclare text_filter CONTENT_SET
FilterProvider text_filter DEFLATE resp=Content-Type /^text.*/
FilterDeclare script_filter CONTENT_SET
FilterProvider script_filter DEFLATE resp=Content-Type /.*script.*/
FilterChain text_filter script_filter
这里FilterDeclare和FilterProvider两个指令的说明都让人看不大明白,如FilterDeclare要求的Filter类型为以下五种:RESOURCE
(the default), CONTENT<sub>SET</sub>, PROTOCOL, TRANSCODE, CONNECTION or
NETWORK.
到哪里去找这五种类型的说明就很让人困惑,最后难道要去看源代码?从源码中找出相关代码,要看懂那也得好长时间。似乎写文档的人认为看档的人一样是Apache的开发者。还好最后在The
Apache Modules
Book中Filter的相关章节中找到了说明,我这才确定使用压缩应该选用的类型的是CONTENT<sub>SET</sub>。FilterProvider是同样的问题,说provider-name应该是由ap<sub>registeroutputfilter函数注册进来的名字</sub>,在文档中没有列出哪些名字可用,这下可好了,只能到源码中去搜,还好mod<sub>deflate的文档中提到了它提供的Filter的名字为DEFLATE</sub>,总算解决了问题。
<h2 id="LDAP、Subversion集成">LDAP、Subversion集成</h2>
与 Apache 2.0 相比,Apache 2.2 改进了认证和授权模块的管理模式,LDAP
支持的配置也略有不同:
- Apache 2.0 使用的LDAP认证模块是:mod<sub>authldap</sub>.so
- Apache 2.2 使用的LDAP认证模块是:mod<sub>authnzldap</sub>.so
需要的Apache模块:
- mod<sub>ldap</sub> :是httpd和LDAP进行交互所以需要的库
- mod<sub>authnzldap</sub>
:是控制LDAP认证和授权的模块,它依赖mod<sub>ldap</sub>。之所以叫authnz是因为这个模块同时实现了authentication和authorization
一些注意事项:
- AuthLDAPBindDN和AuthLDAPBindPassword一般不用设置,前提是LDAP目录允许匿名认证(在slapd.conf中配置的访问控制)
- AuthLDAPURL
中的sAMAccountName是Windows活动目录专有的。如果是OpenLDAP,请改为用户名的属性,如
uid
- AuthLDAPURL
中可以设置LDAP的类过滤器,以提高查询效率。如:objectClass=posixAccount
- Subversion
的认证体系不支持LDAP的组,只支持LDAP的用户。就是说,在配置Subversion
版本库的访问控制策略时,要另外设置一套独立于LDAP的组定义,这会造成数据的不同步。可喜的是,网上已经有牛人写了实用的组同步Python脚本,请参考:Using
LDAP Groups With Subversion's Authz File
- AuthzSVNAccessFile
指示了SVN访问控制的文件。如果该文件有语法问题,整个文件将被SVN忽略,效果等同于没有访问控制文件,此时将出现认证失败的状况。配置该文件时,建议按由少至多调试,还要一次把所有的写上。
- SVN的Location可配置在根目录,即,但应把它放在里面,成为虚拟主机。此时访问地址形如
<h2 id="Squid与WebDAV的整合">Squid与WebDAV的整合</h2>
如果Squid是作前端缓存的,那么无法通过它认证用户并访问后台的WebDAV服务。Squid的参考文档中说了下面这段话,意思我还没有完全理解:auth<sub>param</sub>" target="_blank">http://www.squid-cache.org/Versions/v3/3.1/cfgman/auth_param.html">auth<sub>param</sub>
WARNING: authentication can't be used in a transparently intercepting
proxy as the client then thinks it is talking to an origin server and
not the proxy. This is a limitation of bending the TCP/IP protocol to
transparently intercepting port 80, not a limitation in Squid. Ports
flagged 'transparent', 'intercept', or 'tproxy' have authentication
disabled.
<h2 id="日志统计">日志统计</h2>
- 找出4xx错误并排序
example
grep '".*" 4[0-9][0-9]' access.log | grep -o '".*" 4[0-9][0-9]' | sort | uniq -c | sort -n | tee alog.txt
- Webalizer术语 <http://www.webalizer.org/webalizer_help.html>
Webalizer中的一次visit指的是客户端在指定时间内一直保持对page的请求,两次page的请求的时间间隔不起过指定时间(默认是30分钟)。
每次请求称为Hit,请求的结果不一定是200。File指的是服务器返回有效数据(不是404等)。Page指的是页面,嵌在页面上的图片、声音等文件不算Page。
<h2 id="查看运行时内存">查看运行时内存</h2>
example
for i in ps -edf|grep httpd|awk {'print $2'};do echo -n "httpd $i ==> ";pmap $i|grep total;done
<h2 id="mod<sub>proxyfastcgi模块30秒超时问题</sub>">mod<sub>proxyfastcgi模块30秒超时问题</sub></h2>
<http://apache-http-server.18135.x6.nabble.com/odd-30-second-timeout-when-using-mod-proxy-fcgi-and-php-fpm-td5005245.html>
<http://serverfault.com/questions/500467/apache2-proxy-timeout>
<h1 id="Nginx <span class="tag" tag-name="nginx"><span class="smallcaps">nginx</span></span>">Nginx <span class="tag" tag-name="nginx"><span class="smallcaps">nginx</span></span></h1>
<h2 id="安装">安装</h2>
Ubuntu 14.04安装时还需要装以下包:
example
sudo apt-get install libpcre3 libpcre3-dev zlib1g-dev libssl-dev
libssl-dev不装也能完成configure,但功能会受限。
<h2 id="X-Accel-Redirect From Remote Servers">X-Accel-Redirect From Remote Servers</h2>
X-Accel-Redirect可用于内部定向到远程的资源上。
<http://kovyrin.net/2010/07/24/nginx-fu-x-accel-redirect-remote/>
<h2 id="UserDir">UserDir</h2>
Nginx does not natively support user dirs, but as of 0.7.42 it can be
done by using regex captures.
bash
location ~ ^/~(.+?)(/.*)?$ {
alias /home/$1/public_html$2;
}
Reference: <http://marc.info/?l=nginx&m=123706015914220&w=2>
You may get 403 Forbidden when you just put the snippet above into your
nginx configuration, because by default nginx does not allow autoindex.
If you would like it to behave more likely to the default Apache
userdir, add two lines as below:
bash
location ~ ^/~(.+?)(/.*)?$ {
alias /home/$1/public_html$2;
index index.html index.htm;
autoindex on;
}
<h2 id="调试nginx的配置">调试nginx的配置</h2>
- 打开rewrite<sub>log</sub>
碰到404错误时,打开rewrite<sub>log非常有效</sub>,在error<sub>log中可以查看到与rewrite有关的日志</sub>。
example
rewrite_log on;
- 将错误日志调整为debug
example
error_log /var/logs/nginx/example.com.error.log debug;
- 只针到特定IP输入debug日志
debug日志内容太多,可以通过events只记录特定IP请求的debug日志。
example
events { debug_connection 1.2.3.4;}
- 针对特定location的debug日志
example
location /admin/ { error_log /var/logs/nginx/admin-error.log debug; }
- 用HttpEchoModule来调试 <http://wiki.nginx.org/HttpEchoModule>
<h2 id="fastcgi配置">fastcgi配置</h2>
<h3 id="fastcgi<sub>params中SCRIPTFILENAME</sub>">fastcgi<sub>params中SCRIPTFILENAME</sub></h3>
Ubuntu14.04软件源中的nginx包的fastcgi<sub>params文件包含如下一行配置</sub>:
example
fastcgi_param SCRIPT_FILENAME $request_filename;
使用了nginx.org软件仓库中的nginx,对应的fastcgi<sub>params中却没有这一行</sub>。由此会产生奇怪的后果,使用nginx转发至php-fpm时,php-fpm返回的内容为空,而浏览器端收到的响应居然是200。这个问题在下面的网址中有详细描述:
<http://www.cnxct.com/php-return-empty-result-on-nginx-without-script_filename/?utm_source=tuicool&utm_medium=referral>
使用nginx.org时务必注意SCRIPT<sub>FILENAME参数的配置</sub>。
<h3 id="fastcgi<sub>splitpathinfo</sub>">fastcgi<sub>splitpathinfo</sub></h3>
Ubuntu 16.04发生版中的nginx在配置php
fpm时,使用了名为snippets/fastcgi-php.conf的配置文件:
# regex to split $uri to $fastcgi_script_name and $fastcgi_path
fastcgi_split_path_info ^(.+\.php)(/.+)$;
# Check that the PHP script exists before passing it
try_files $fastcgi_script_name =404;
# Bypass the fact that try_files resets $fastcgi_path_info
# see: http://trac.nginx.org/nginx/ticket/321
set $path_info $fastcgi_path_info;
fastcgi_param PATH_INFO $path_info;
fastcgi_index index.php;
include fastcgi.conf;
从这个配置可以看出,它会先把路径用fastcgi<sub>splitpathinfo参数配置的正则来解析</sub>,然后就会去测试从正则中解析得到\$fastcgi<sub>scriptname在磁盘上是否存在</sub>。这时,如果想要用location中的正则来确定fastcgi<sub>scriptname的话</sub>,就总是会得到404了。
我希望能够配置针对每个用户特定目录下的PHP脚本,这时就只能跳过这个文件。
location ~ ^/php([0-9a-z]+)/(.*\.php$) {
fastcgi_pass unix:/run/php/php7.0-fpm.sock;
#fastcgi-php.conf中为了获取path_info,使用了try_files $fastcgi_script_name =404,由于该文件的路径不在/var/www下,就会找不到该文件,从而产生404.
# include snippets/fastcgi-php.conf;
include fastcgi.conf;
fastcgi_param SCRIPT_FILENAME /home/$1/php/$2;
fastcgi_index index.php;
}
<h2 id="淘宝定制的Tengine">淘宝定制的Tengine</h2>
<h1 id="Squid <span class="tag" tag-name="squid"><span class="smallcaps">squid</span></span>">Squid <span class="tag" tag-name="squid"><span class="smallcaps">squid</span></span></h1>
<h2 id="术语">术语</h2>
- ICP Internet Cache Protocol is a simple web-caching protocol used to
query proxy servers (cache peers) about the existence of a
particular object in their cache. 主要缺陷是:latency,false
hit,不适用peer较多的环境。(Squid Proxy Server 3.1 P216)
- HTCP Hypertext Caching
Protocol用于发现HTTP缓存,并提供代理服务器之间的通信。相比于ICP有更多优点(Squid
Proxy Server 3.1 P218)。
- Cache Digest Bloom Filter算法的实现。
- WCCP或WCCP2
WCCP是Cisco路由器专用的一种网络协议,它可以拦截HTTP包,并重定向到新的代理服务器。
<h2 id="提升Squid内存使用的配置">提升Squid内存使用的配置</h2>
单纯配置cache<sub>mem并不能让Squid用内存来做缓存</sub>,还有以下的一些配置选项也很有关系:
example
memory_pools On 默认即是打开的。
memory_pools_limit 100 MB 默认为5MB。
maximum_object_size 8192 KB 对于超过maximum_object_size大小的对象,Squid不会将其存入磁盘cache_dir,默认为4MB。
maximum_object_size_in_memory 4096 KB 对于超过maximum_object_size_im_memory大小的对象,Squid不会尝试将其放在内存cache中。
<h2 id="重建某个cache<sub>dir下所有缓存</sub>">重建某个cache<sub>dir下所有缓存</sub></h2>
example
squid -k shutdown
cd /usr/local/squid/var
mv cache oldcache
mkdir cache
chown nobody:nobody cache
squid -z
squid -s
rm -rf oldcache &
如果cache<sub>dir在单独的分区中</sub>,那么可用mkfs(or
newfs)直接重建文件系统。
<h2 id="清除swap.state">清除swap.state</h2>
Squid在swap.state文件中记录了cache<sub>dir中所有的对象</sub>,如果覆盖这个文件,就能让Squid忘记所有的cached对象。然而不能简单地删除swap.state,如果找不到这个文件,Squid会重新扫描cache<sub>dir</sub>。也不能简单地truncate这个文件。正确做法是
example
echo '' > /usr/local/squid/var/cache/swap.state
When Squid reads the swap.state file, it gets an error because the
record that should be there is too short. The next read results in an
end-of-file condition, and Squid completes the rebuild procedure without
loading any object metadata.
<h2 id="用PURGE方法删除个别缓存对象">用PURGE方法删除个别缓存对象</h2>
Squid实现了并非HTTP标准的PURGE方法,对于DELETE方法,由于它是HTTP标准方法,Squid会转发到原来的服务器上,而PURGE方法是Squid专用来清除指定缓存对象的。默认情况下Squid会禁止PURGE访求,因为这会删除缓存对象,因此要使用PURGE方法,需要先进行配置。
example
acl cachemanager src 10.2.4.0/24 127.0.0.1
acl purge method PURGE
http_access allow cachemanager purge
http_access deny purge
接下来用squidclient命令清除指定缓存即可:
example
squidclient -h 10.2.3.67 -p 80 -m PURGE http://10.2.3.67/
<h2 id="X-Cache和X-Cache-Lookup">X-Cache和X-Cache-Lookup</h2>
X-Cache - this shows whether an object is available or not.
X-Cache-Lookup - this keeps the result of a store table lookup before
refresh causing rules are checked (i.e. it indicates if the object is
available before any validation would be attempted).
在配置Moodle的前端Squid服务器时碰到一个问题:
类似于http://10.2.3.67/moodle/theme/image.php?theme=fusion&image=help&rev=468
的小图片在浏览器刷新时,不用Squid和使用Squid的行为不同。不用Squid,则不会传递图片,服务器端直接返回304。如果使用了Squid,则服务器端返回200,效率上略有损失。仔细分析里面的HTTP头,发现刷新时会同时发送If-None-Match和If-Modified-Since,根据HTTP
1.1的规定,此时服务器必须不能返回304。这样看来使用Squid符合规范。然而通过配置Apache服务器,让服务器在第一次传递回图片时去掉Last-Modified头,使用Squid的情况下还是只能得到200。这个问题非常困惑,不过Moodle主要的性能问题不在于这些,而在于PHP脚本的执行还很慢,估计是数据库慢导致的问题,因此优化主要应该在数据库层面进行。
<h2 id="refresh<sub>pattern</sub>">refresh<sub>pattern</sub></h2>
refresh<sub>pattern可令刚过期的对象在短期内保持fresh</sub>,由此提高命中率。另外,refresh<sub>pattern还可以覆盖原Web服务器的HTTP头</sub>。如果用refresh<sub>pattern来缓存本不能缓存的对象</sub>,或者延长了缓存对象的生存时间,都可能会导致无法预料的结果,这样使用时需要慎重。
cache指令只能控制某个请求能否被缓存,而refresh<sub>pattern可以修改缓存对象的生存时间</sub>。
<h2 id="Access Control">Access Control</h2>
<h3 id="Access List(ACL)">Access List(ACL)</h3>
- 如果某条ACL规则有多个元素,Squid使用OR(或者)连接,因此有必要把最常匹配到的元素放在前面。
多个普通元素会按splay
tree结构存放,因此元素的平均比较次数为log(n)。然而如果元素都是正则表达式,则需按序比较,因此大量的正则表达式会导致效率的损失。
- 元素的数量如果过多,可以考虑使用下面的格式:
example
acl aclname acltype "file" ...
在文件中可以放入多个元素,每个元素一行,还可以在文件中添加注释(#号开头)。
- time ACL type
example
acl aclname time [day-abbrevs] [h1:m1-h2:m2]
<h3 id="Access Control Rule">Access Control Rule</h3>
- AND ACL规则的多个元素之间是OR关系,而Access Control
Rule中的多个ACL规则是AND关系。比如:
example
access_list allow ACL1 ACL2 ACL3
要应用此条规则的请求必须同时满足ACL1,ACL2和ACL3,Squid依序检查,只要找到不满足的ACL,后面的ACL不会继续匹配。
- ACL的次序很重要
example
acl A method http
acl B port 8080
http_access deny A B
由于规则A匹配的几率极大,把A放前面的效率较差,写成下面这样就好多了:
example
http_access deny B A
- Delayed Checks Squid在碰到 ident, dst, srcdomain, and
proxy<sub>auth</sub>
等规则时无法直接判断,Squid此时会发出查询,等到结果后再重新检查。需要特别注意的是,Squid不会从原先中断的位置继续往下验证,而是把所有规则从头再验证一遍。
<h3 id="使用LDAP认证用户">使用LDAP认证用户</h3>
<http://wiki.squid-cache.org/KnowledgeBase/LdapBackedDigestAuthentication>
<http://wiki.squid-cache.org/ConfigExamples/Authenticate/Ldap>
<http://wiki.squid-cache.org/HelpOnAuthentication/LDAP>
测试basic<sub>ldapauth命令</sub>
example
./basic_ldap_auth -b 'ou=people,dc=jhc,dc=cn' -u 'uid' -D 'cn=Manager,dc=jhc,dc=cn' -w pass -h host -d -v 3 -f '(uid=%s)'
接下来要输入用户名和密码,中间用空格分隔。
<h2 id="log">log</h2>
example
access_log daemon:/var/log/squid/access.log squid
配置完成后,如果再设置logrotate,会导致squid不再往日志文件写入数据,必须调用squid
-k reconfigure。
<h2 id="SMP支持">SMP支持</h2>
<http://wiki.squid-cache.org/Features/SmpScale>
Squid从3.2开始支持SMP,workers配置项可用于启动多个进程,这些进程对外看起来像是一个,而每个worker可有自己特有的配置。
默认情况下,多个worker可共享HTTP, HTTPS, SNMP, ICP, and
HTCP监听地址。其中ICP和HTCP的客户端配置时需要注意避免源地址冲突。DNS地址不会共享。多个worker可共享log。内存cache自动被多个worker共享,cache<sub>dir中只有rock</sub>
storage type可被共享,其余类型的storage
type必须每个worker单独配置。Cache
Manager的统计数据基本是针对worker的,也有少量报告会综合各个worker。SNMP的统计是所有worker综合的结果。
<http://www.squid-cache.org/Versions/v3/3.2/RELEASENOTES.html>
SMP配置中,cache<sub>dir不能出现在workers之前</sub>,不使用rock的情况下,需要通过macro和conditional配置为每个worker配置单独的cache<sub>dir</sub>。例如:
example
workers 2
if ${process_number} = 1
cache_dir diskd /cache1 130000 128 512
else
cache_dir diskd /cache2 130000 128 512
endif
memory<sub>cacheshared用于控制是否在worker之间共享内存</sub>,目前的版本(3.2.3)中,超过32KB的entry无法共享。
<http://wiki.squid-cache.org/Features/RockStore>
启用worker之间共享内存需要在fstab中做以下配置:
example
shm /dev/shm tmpfs nodev,nosuid,noexec 0 0
<h2 id="configure脚本">configure脚本</h2>
CXXFLAGS设置时使用"-O3"级优化,squid在编译时会出错。
机房访问外网代理服务器configure配置:
example
./configure --disable-ident-lookups --enable-auth --enable-digest-auth-helpers=ldap --disable-htcp --disable-ipv6 --prefix=/usr/local/squid --enable-storeio="diskd,aufs" --enable-removal-policies="lru,heap" --disable-wccp --disable-wccpv2 --disable-auth-ntlm --disable-translation
<h1 id="ntp <span class="tag" tag-name="ntp"><span class="smallcaps">ntp</span></span>">ntp <span class="tag" tag-name="ntp"><span class="smallcaps">ntp</span></span></h1>
<h2 id="中国国家授时中心(www.time.ac.cn)时间服务器IP:210.72.145.44">中国国家授时中心(www.time.ac.cn)时间服务器IP:210.72.145.44</h2>
218.21.130.42 (cn.pool.ntp.org的IP)
<h2 id="不安装ntp服务器,直接用客户端更新的办法">不安装ntp服务器,直接用客户端更新的办法</h2>
- ntpdate 210.72.145.44 校准系统时间
- hwclock -w 将系统时间写入BIOS
- 设置定时任务,每天的某个时间去校准 5 0 \* \* \* /usr/sbin/ntpdate
210.72.145.44&&/sbin/hwclock -w
<h2 id="几个ntp常用工具">几个ntp常用工具</h2>
- ntpstat 查询是否与上层服务器顺利连接
- ntptrace 列出目前ntp服务器与上层ntp服务器之间的关系
- ntpq -p 列出相关的ntp服务器的状态
<h2 id="ntpd服务启动后至少过10分钟左右,客户端才能连接上来更新时间">ntpd服务启动后至少过10分钟左右,客户端才能连接上来更新时间</h2>
<h1 id="Security <span class="tag" tag-name="security"><span class="smallcaps">security</span></span>">Security <span class="tag" tag-name="security"><span class="smallcaps">security</span></span></h1>
<h2 id="pam <span class="tag" tag-name="pam"><span class="smallcaps">pam</span></span>">pam <span class="tag" tag-name="pam"><span class="smallcaps">pam</span></span></h2>
<h3 id="PAM的组成部分">PAM的组成部分</h3>
- 传统的Unix认证程序。如login和passwd,在PAM中被称为服务。
- 执行各种特定认证任务的模块。这些.so文件在/lib/security下,每个模块仅负责认证的一小部分,执行后,某个模块把执行结果返回给PAM,指出是否允许用户访问。模块还有可能返回中性值。
- 配置文件。用于表示对所支持的服务执行什么认证过程,在/etc/pam.d/目录下,文件名与服务名相同,或用单个文件/etc/pam.conf。
- 某些PAM模块需要的额外的配置设置,在/etc/security目录下。
<h3 id="PAM提供的四种 Management Group">PAM提供的四种 Management Group</h3>
- Auth Group (Authentication service modules) Auth
Group提供两种功能,一是验证用户(一般是通过用户名和密码),二是授权凭证(credential),credential中包含了组的信息。
- Account Group (Account management modules) Account
Group提供对帐号的控制,如某个帐号是否已经过期,规定每个星期某个服务只能使用若干次,或只能在一天的某个特定时间里使用该服务等。
- Session Group (Session management modules) Session
Group用于创建指定服务所需的环境,比如可以设定用户登陆后自动mount某个加密后的文件系统,用户登出后再自动unmount该文件系统。
- Password Group (Password management modules) Password
Group只被用于当用户需求更改密码的情况。 所谓Management
Group即是PAM提供的认证过程的四个阶段。某个PAM模块可以提供一个或多个Management
Group的功能,但一般可以把提供不同Management
Group功能的同一个PAM模块当作是不同的模块。
<h3 id="PAM的服务名">PAM的服务名</h3>
服务的名字是由应用程序自身决定的,应用程序通过调用pam<sub>start函数时传入服务名</sub>,并不能通过配置文件修改。
example
extern int PAM_NONNULL((1,3,4))
pam_start(const char *service_name, const char *user,
const struct pam_conv *pam_conversation,
pam_handle_t **pamh);
服务名“OTHER"是保留的名称,当某个应用程序请求的服务名在配置中找不到时,就会默认使用"OTHER"。
<h3 id="工具">工具</h3>
- RedHat系列有authconfig软件包,可用authconfig进行配置,带图形界面的命令称为authconfig-tui。
- pamtester为测试工具。
<h2 id="sasl <span class="tag" tag-name="sasl"><span class="smallcaps">sasl</span></span>">sasl <span class="tag" tag-name="sasl"><span class="smallcaps">sasl</span></span></h2>
<h3 id="Authentication and authorization identifiers">Authentication and authorization identifiers</h3>
sasl区分authentication id和authorization id。authorization
id即是userid,是一个系统用以确认用户的id。authentication
id是系统用以验证密码的id。假设userid为A的用户想让B用户帮自己处理事情,就要让B具有A所有的权限,同时两者的userid又保持不同,这时,就需要让A和B有相同的authentication
id。
配置svnserve.conf中如果指定min-encryption不为0,则在mech<sub>list中只有PLAIN的话</sub>,svnserve提示错误信息:svnserve:
auxpropfunc error invalid parameter supplied。
<h3 id="Cyrus SASL Components">Cyrus SASL Components</h3>
- Plugins (SASL Mechanisms)
Mechanism插件通常由IETF标准化组织定义(NTLM除外),这与其余插件有很大不同。像auxprop等插件主要是为其他插件提供数据库服务或用户名规范化服务,是Cyrus特有的实现。
- Password Verification Mechanisms
PLAIN
- Shared Secret Mechanisms
CRAM-MD5, DIGEST-MD5, and SRP
- Kerberos Mechanisms
- Plugins (Auxiliary Property)
Auxiliary Property简称auxprop,它为glue layer提供了数据库服务。Cyrus
SASL自带两个auxprop插件:SASLdb和SQL。它经常被用在Shared Secret
Mechanism中用来访问“userPassword”属性。
- Password Verification Services
Password Verifier的功能是确认用户名和密码。它无法用于Shared Secret
Mechanism,因为它无法验证Hash值。Cyrus SASL自带了两种Password
Verifier,通过pwcheck<sub>method选项指定</sub>:
- auxprop
利用auxprop插件来访问密码,并和客户端提供的密码进行比对,给出比对结果。
- saslauthd
saslauthd自带了若干模块用于验证密码,如PAM,LDAP,Kerberos。想用/etc/shadow来验证用户就需要用到saslauthd服务了。
<h3 id="svn配置saslauthd问题 <span class="tag" tag-name="svn"><span class="smallcaps">svn</span></span> <span class="tag" tag-name="saslauthd"><span class="smallcaps">saslauthd</span></span>">svn配置saslauthd问题 <span class="tag" tag-name="svn"><span class="smallcaps">svn</span></span> <span class="tag" tag-name="saslauthd"><span class="smallcaps">saslauthd</span></span></h3>
saslauthd设置机制为pam,用testsaslauthd来验证saslauthd时发现验证时必须加入服务名,如:
example
testsaslauthd -u username -p pass -s svn #在/etc/pam.d目录下必须有对应的svn配置文件
testsaslauthd -u username -p pass -s login #在/etc/pam.d目录下必须有对应的login配置文件
问题来了,pam的服务名是不能配置,而由程序代码指定的,svn这个名字是怎么指定的呢?
为了让svn使用sasl认证,并且使用saslauthd来认证用户,就需要在/etc/sasl2/下创建svn.conf文件:
example
pwcheck_method: saslauthd
log_level: 7
mech_list: PLAIN LOGIN
换个名字行不行?saslauthd如何将svn.conf文件和subversion的服务对应起来?
经过修改svn.conf文件名字等测试手段,可以发现/etc/sasl2/svn.conf这个文件与saslauthd进程没有什么关系,应该是svnserve配置文件默认的一部分。/etc/sasl2/svn.conf文件更名或移动都会在日志中发现svnserve的错误信息。
<h1 id="OpenLDAP <span class="tag" tag-name="openldap"><span class="smallcaps">openldap</span></span>">OpenLDAP <span class="tag" tag-name="openldap"><span class="smallcaps">openldap</span></span></h1>
<h2 id="OpenLDAP安装">OpenLDAP安装</h2>
由于openldap需要Berkeley DB来存放数据,所以需先安装Berkeley DB
4.2.52,可到它的网站下载,网址见上面。运行下面的命令解压:
解完压后,会生成一个db-4.2.52目录,进行该目录下的build<sub>unix目录</sub>。执行以下命令进行配置安装。
默认安装在/usr/local/BerkeleyDB.4.2目录下。安装完成后,
要把/usr/local/BerkeleyDB.4.2/lib的库路径加到/etc/ld.so.conf文件内,添加完成后执行一次
ldconfig,使配置文件生效。这样编译openldap时才能找到相应的库文件。这样资料库就安装完成了,接下来可以安装openldap了。
ld.so.conf是什么东西?它就是系统动态链接库的配置文件。此文件内,存放着可被LINUX共享的动态链接库所在目录的名字(系统目录/lib,
/usr/lib除外),各个目录名间以空白字符(空格,换行等)或冒号或逗号分隔。一般的LINUX发行版中,此文件均含一个共享目录/usr
/X11R6/lib,为X window窗口系统的动态链接库所在的目录。
ldconfig是它的管理命令,具体操作方法可查询man手册,这里就不细讲了。
到openldap官方网站下载最新的稳定版源码,并解压。查看INSTALLT
和README文档,这个很重要,因为安装方法和一些注意事项都在里面有介绍。认真弄明白文档内容能节省你不少的安装调试时间。这也是开源软件的一个特
点,给用户提供了最大的灵活性和可配置性。但也增加了系统安装配置的难度,需要有相关的文档配置说明和指导。在官方网站上还有详细的帮助文件,在整个系统
配置中需要经常查询。
解压完成后,会生成一个openldap-2.1.29目录。进行该目录,执行以下命令进行配置安装。
LDFLAGS="-L/usr/local/BerkeleyDB.4.2/lib" ./configure
–prefix=/usr/local/openldap –enable-ldbm
注意以上配置语句,要设置资料库的include和lib路径,否则在配置到资料库相关内容时会提示Berkeley
DB版本不兼容,并中断配置。如果没有–enable-ldbm选项,在make
test时会提示ldbm找不到。为了减少出错,还是加上为好。 \#make depens
\#make \#make test 在make test阶段要花费较长时间进行测试,好像有16项吧。
\#make install
通过配置命令可以看出,我们把openldap安装到/usr/local/openldap目录下。建议以源码安装的软件都放到独立的目录下,不要放到软件默认的目录。好处是方便管理和控制,所有文件在统一的目录下,卸载软件只要删除整个目录就可以了。
<h2 id="OpenLDAP配置">OpenLDAP配置</h2>
<h3 id="OpenLDAP2.3之后版本的配置">OpenLDAP2.3之后版本的配置</h3>
OpenLDAP自2.3以后的配置已经和Active
Directory相似了,不再用专门的配置文件和schema文件,而是用自带的目录条目来描述这些信息。下面描述的配置依旧是传统方式的配置,以下
slapd.conf显示了一个典型的配置: database ldbm suffix
"dc=wiremonkeys,dc=org" rootdn "cn=ldapguy,dc=wiremonkeys,dc=org" rootpw
{SSHA}zRsCkoVvVDXObE3ewn19/Imf3yDoH9XC directory /var/lib/ldap
网络上大多数资料都没有讲解rootdn的设定有什么含义,以上的例子用的是Linux
Server Security
第2版的例子,对这些条目讲解得比较清楚。rootdn和rootpw用于设定LDAP数据库的管理员的用户名和密码,但也是LDAP数据库中一条特殊的
entry,用户在查询LDAP时是不会看到这个条目的。初学时一直以为rootdn的定义是不是非得用uid属性或是cn属性,现在看起来只要
suffix对得上,使用其他什么属性应该是很自由的。rootpw的生成可用slappasswd
-h {SSHA}命令来生成。
slapd在启动时先找有没有slapd.d目录,找不到该目录再找有没有slapd.conf文件。目录优先,如果要使用slapd.conf,可把slapd.d目录删除。
slapd.d的配置是用LDAP来管理LDAP服务器,好比用关系数据库来管理关系数据库系统一样。
其中olc是很多配置属性的前缀,它是OpenLDAP
Configuration的缩写。对于OpenLDAP来说,所用各个schema本无次序,而{X}强制约定了次序,一般情况下这些{X}序号会根据条目创建时间的先后自动生成。
新的配置方法不再是编辑slapd.conf文件,而是创建某个ldif文件(如config.ldif),同时创建好配置目录(如/etc/ldap/slapd.d),再用下面的命令对配置进行初始化:
example
slapadd -F /etc/ldap/slapd.d -n 0 -l config.ldif
参数-n后的数字表示配置文件中的第n个数据库。
也可以用下面的命令将slapd.conf文件的配置转换成新的配置:
example
slaptest -f /etc/ldap/slapd.conf -F /etc/ldap/slapd.d
测试slapd.conf文件的正确性可用下面命令:
example
sudo slaptest -v -f slapd.conf
<h3 id="初始domain">初始domain</h3>
Root
DSE需要使用特定的容器类型,像OrgnizationUnit之类的类型是不能作为顶层容器的。在slapd.conf中配置好suffix,rootdn,rootpw,并不会自动生成顶层容器。安装OpenLDAP软件,并设置好slapd.conf之后,最好使用下面的步骤初始化容器。
- 关闭slapd服务,删除已经生成的数据库
example
sudo rm -f /var/lib/ldap/*
- 创建好初始的容器ldif文件
<!-- -->
File Edit Options Buffers Tools Help
dn: dc=jhc,dc=cn
dc: jhc
objectClass: domain
objectClass: top
structuralObjectClass: domain
- 用slapadd添加初始ldif文件
example
slapadd -v -l init.ldif
- 启动slapd服务
<h3 id="Debian下cn=config的查询与修改">Debian下cn=config的查询与修改</h3>
example
ldapsearch -Y EXTERNAL -H ldapi:/// -b "cn=config"
ldapmodify -Y EXTERNAL -H ldapi:/// -f
默认情况下,Debian的OpenLDAP服务器配置为只允许超级用户使用上面两个命令,因此在执行时还需加上sudo。
<h3 id="配置Linux使用LDAP认证用户">配置Linux使用LDAP认证用户</h3>
- redhat系列服务器可使用authconfig来简化配置。
RHEL6的配置比较特别,需要设置sssd
example
auth_provider = ldap
如果设置auth<sub>provider为ldap</sub>,则必须设置下面此项
example
ldap_access_filter = memberOf=cn=Admin,ou=groups,dc=jhc,dc=cn
如果不设定使用SSL,则必须将/etc/sysconfig/authconfig中的FORCELEGACY设为yes。同时不要忘记执行authconfig
–update。 配置完成后可用下面的命令查看系统中所有的用户
example
getent passwd
- Debian系列可以使用auth-client-config
auth-client-config是一组用于配置pam和NSS的脚本。NSS相关内容可查看libc的info文档。getent命令可查询NSS数据库。
<http://askubuntu.com/questions/127389/how-to-configure-ubuntu-as-an-ldap-client>
具体做法:
- 软件安装: : apt-get install ldap-utils libpam-ldap libnss-ldap
nslcd 安装过程中提示填写ldap相关信息:ldap server 地址,base
dc等,信息保存在/etc/ldap.conf中,可通过dpkg-reconfigure
ldap-auth-config重新配置。
- 认证方式中添加ldap: : root@ldapclient:~# auth-client-config -t
nss -p lac<sub>ldap</sub>
- 使认证通过后自动创建用户家目录: vi
/etc/pam.d/common-session,追加内容: : session required
pam<sub>mkhomedir</sub>.so skel=/etc/skel umask=0022
- 配置开启ldap认证方式,更新pam : root@ldapclient:~#
pam-auth-update
- 执行 : update-rc.d nslcd enable
- 配置可在本机通过passwd更改用户密码: vi
/etc/pam.d/common-password,除去其中的use<sub>authtok参数</sub>
- 重启相关服务: : /etc/init.d/nscd restart
- 登陆或切换用户时即通过ldap进行认证,如切换为ldap中的用户manager:
: user1@ldapclient:~\$ su - manager : Password:\*\*\*\*\* :
Creating directory '/home/manager'. : manager@ldapclient:~\$
<h3 id="配置日志输出">配置日志输出</h3>
example
logfile /var/log/ldap/slapd_debug.log
loglevel stats stats2
需要注意的是logfile只是输出slapd的debug信息,有点像squid.out文件。真实日志的输出需要在系统的日志输出管理器中统一配置。rsyslog配置如下:
example
local4.* /var/log/ldap/slapd.log
openldap默认使用local4,也可通过slapd的启动参数加以修改。
<h3 id="配置只监听本地端口">配置只监听本地端口</h3>
slapd.conf文件中无法实现这个要求,可以通过slapd的启动参数来加以限制。 -h
<ldap://127.0.0.1:389/>
<h3 id="自定义objectclass的技巧">自定义objectclass的技巧</h3>
初学目录服务器,已有的objectclass太多,但到一个具体问题时,却觉得已有的这些objectclass经常不适用,比如我要拿LDAP服务器
用作用户验证,并保存适量的信息,已有的person、inetorgperson等都不太适用,若是要自己来定义attribute,那可真不如用关系数据库了,attribute的规则太多,并是学习就又得花费不少功夫。Linux
Server
Security介绍一种方法,让我开了眼界,扫清了很多障碍。原来要用到什么属性,只要用grep命令到schema目录下的各个文件中查找相关的属性就可以了,再把这些属性整合成一个objectclass就行了,毕竟,定义一个objectclass还是很轻松的。用于登陆验证的
objectclass只要包括uid和userpassword两个属性就可以了。
<h3 id="配置TLS">配置TLS</h3>
example
cd /var/myca
CA.sh -newca #使用openssl自带的CA.sh脚本
openssl req -new -nodes -keyout newreq.pem -out newreq.pem
(The -nodes argument above prevents encryption of the private key.
OpenLDAP only works with unencrypted private keys.) Then use your CA to
sign this cert request:
example
CA.sh -sign
Finally you need to install these certs for use with OpenLDAP Software.
Assuming your OpenLDAP Software was installed in /usr/local:
example
cp cacert.pem /usr/local/etc/openldap/cacert.pem
mv newcert.pem /usr/local/etc/openldap/servercrt.pem
mv newreq.pem /usr/local/etc/openldap/serverkey.pem
chmod 600 /usr/local/etc/openldap/serverkey.pem
Then add these lines to /usr/local/etc/openldap/slapd.conf:
example
TLSCACertificateFile /usr/local/etc/openldap/cacert.pem
TLSCertificateFile /usr/local/etc/openldap/servercrt.pem
TLSCertificateKeyFile /usr/local/etc/openldap/serverkey.pem
You must also install a copy of the CA certificate on all of your client
machines. Configuration is done in /usr/local/etc/openldap/ldap.conf:
example
TLS_CACERT /usr/local/etc/openldap/cacert.pem
<h2 id="概念">概念</h2>
<h3 id="LDAP协议">LDAP协议</h3>
- tag
在访问日志中看到的tag=97标识的返回数据的类型,其实也反应了operation的类型,日志中看到的tag是LDAP协议中使用的BER
tag。常见tag如下:
| Tag | Description |
|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| tag=97 | A result from a client bind operation. |
| tag=100 | The actual entry being searched for. |
| tag=101 | A result from a search operation. |
| tag=103 | A result from a modify operation. |
| tag=105 | A result from an add operation. |
| tag=107 | A result from a delete operation. |
| tag=109 | A result from a moddn operation. |
| tag=111 | A result from a compare operation. |
| tag=115 | A search reference when the entry on which the search was performed holds a referral to the required entry. Search references are expressed in terms of a referral. |
| tag=120 | A result from an extended operation. |
<h3 id="术语">术语</h3>
- DIT Directory Information Tree
- RDN Relative Distinguished Name
- DSA Directory Server Agent
- DSE DSA Specific Entry
- Root DSE Root Directory Server Entry, Servers supporting LDAPv3
support the Root Directory Server Entry (RootDSE) which is
represented by an empty distinguished name (““). This search base,
combined with other search criteria, returns important information
about the server’s capabilities. Other special search bases exist
for retrieving server schema and monitoring information.
- directory suffix all entries in the directory have a common ancestor
called “directory suffix”.
- database and backend
database指的是存放目录信息树的位置,如文件,关系数据库,网络位置等。backend指用于保存数据的机制,它一般以模块方式编译,可在运行时动态加载。
- ldapi ldapi:// is LDAP over (local) IPC. Currently the only IPC
mechanism is Unix domain (PF<sub>LOCAL</sub>) streams.
- ldaps <ldaps://> is LDAP over SSL (Secure Socket Layer). It is
deprecated in favor of LDAPv3's Start TLS.
- DN和RDN
LDAP中的命名模型,也即LDAP中的条目定位方式。在LDAP中每个条目均有自己的DN和RDN。DN是该条目在整个树中的唯一名称标识,RDN是条目在父节点下的唯一名称标识,如同文件系统中,带路径的文件名就是DN,文件名就是RDN。
<h3 id="四个模型">四个模型</h3>
LDAP实现了四个模型,这四个模型也代表了看待LDAP的四种不同的角度:Information
model,Naming model,Functional model,Security model。
<h3 id="Information Model">Information Model</h3>
信息的基本单位是entry,所谓的目录服务就是提供entry的信息,而entry是通过object
class来定义的。从面向对象编程语言的角度来看,schema中object
class的定义就相当于类的定义,而具体的entry就是根据这个类的定义而生成的对象,这里只不过是名词上的不同。object
class定义了各种属性(attribute),每个attribute都包括attribute
name和attribute
value(一个name可对就多个value)。比如"top"定义了一个"objectClass"属性,而每个entry的"objectClass"属性可同时有多个值。
objectClass的类型共有三种,分别是ABSTRACT,STRUCTURAL和AUXILIARY,其中STRUCTURAL是默认的,也是常用的存储在Directory
Information
Tree中的类型。ABSTRACT相当于所谓的抽象类,可被继承,但不可实例化的,比如最顶层的"top"。AUXILIARY可对现有的模型进行扩展,可存放schema中定义的任意属性,如subschema
object(用以存放目录服务器的schema,Active
Directory似乎就是这种方式来实现的,没有schema文件),dcObject(Domain
Component Object,只有auxiliary object可混全合使用dcObject和o)。
Attribute Type的定义中,EQUALITY,ORDERING和SUBSTR均为匹配规则(matching
rules),主要用以确定属性的相等、排序、取子串等操作的匹配规则,这些规则已经有标准实现,不必自己去实现。COLLECTIVE表示某个entry的属性存放在某个特定的subentry中,比如一个传真号码是整个公司都相同的,就可放在一个单独的subentry中。USAGE定义了属性的用法,主要是以下三种:
example
AttributeUsage="userApplications" | "directoryOperation"
| "distributedOperation" ; DSA-shared
| "dSAOperation" ; DSA-specific, value depends on server
其中,userApplications属于user
attribute,是用户可修改的属性,而其余三种属于operational
attribute,用户不可修改,由目录服务器管理,如modifyTimeStamp表示本纪录上次被修改的时间。
<h2 id="Search criteria">Search criteria</h2>
LDAP客户端为了从directory server中获取信息,需要提供以下search
criteria:
- What part of the directory tree will be searched.
- What qualities must returned entries contain or not contain.
- What information from matching entries should be returned.
search criteria至少要由以下三部分组成:Base,Scope,Filter。
<h3 id="WHERE TO SEARCH: BASE AND SCOPE">WHERE TO SEARCH: BASE AND SCOPE</h3>
base和scope决定了directory tree中哪部分内容会被搜索。serach
base即是搜索范围内最高节点的distinguished
name,如ou=Authors,dc=manning,dc=com。search scope决定了search
base之下还有多少节点会在接下来的匹配范围内。LDAP协议定义了三种search
scope:Base, One-level, Subtree。
<h3 id="WHAT TO EVALUATE: SEARCH FILTERS">WHAT TO EVALUATE: SEARCH FILTERS</h3>
- LDAP search filter共有七种:
- Presence
要求节点在指定的属性上有值(值可以任意),如sn=\*
- Equality
要求节点在指定的属性上有指定的值,如sn=Johnson
- Substring
这里\*的用法与一般用法不同,只有三种匹配:initial
substring(如sn=Johns\*),middle substring(如sn=\*ohns\*),end
substring(如sn=\*son)。可以把这三种组合起来,但是像“\*D\*ln\*ey”这样的字符串是不对的,因为它用了两次middle
substring。
- Greater-than or equal
- Less-than or equal
- Approximate
近似匹配的结果很难预知,具体算法由服务器来实现(如soundex)。sn~=Donnelley
- Extensible
(sn:1.2.3.4.5=Hartman),这个filter表示用1.2.3.4.5匹配规则(Matching
Rule)进行匹配,匹配的条件是sn=Hartman,如果服务器支持1.2.3.4.5匹配规则,即会返回相应的节点。
- and、or和not的用法
(&(sn=Smith)(title=President))
(&(sn=Smith)(title=President)(cn=Jim\*))
(\|(sn=Smith)(title=President))
(&(\|(objectClass=person)(objectClass=inetOrgPerson))(sn=Smith))
(!(sn=Smith))
<h3 id="THE ATTRIBUTE RETURN LIST">THE ATTRIBUTE RETURN LIST</h3>
不管attribute return
list如何设置,dn的值总是会返回,事实上dn并非节点的属性。不同的应用程序对目录服务器的需求是不同的,有的应用只需要uid,
userPassword,这时如果把图片之类的数据也传过来,就会导致效率问题,控制attribute
return list能够有效解决这类问题。
<h2 id="Security">Security</h2>
<h3 id="Connection Security">Connection Security</h3>
保证客户端与服务器之间通信的安全。配置SSL和TLS。
<h3 id="Authentication">Authentication</h3>
用于认证用户,确保用户确实是他所声称的那个人。OpenLDAP中提供两种认证方式:Simple和SASL。
- SASL
<http://www.openldap.org/doc/admin24/sasl.html>
- 配置DIGEST-MD5
DIGEST-MD5需要使用明文存放密码,先由服务器生成challenge,然后客户端的response表明自己知道密码是什么,整个过程中密码不会通过网络传送。这种方式称为Shared
Secret Mechanism。使用此种方式需要对服务器端存放密码的文件或LDAP
Entry做访问权限的保护。 OpenLDAP中使用DIGEST-MD5
SASL认证可以选择用sasldb来保存密码,或在LDAP服务器中保存密码。前者可能涉及到saslauthd,我还没有测试成功过。在LDAP服务器中保存密码的配置步骤如下:
- 添加用于SASL认证的帐号
example
dn: uid=authenticate,ou=system,dc=jhc,dc=cn
uid: authenticate
ou: system
userPassword: XXXXXX
objectClass: account
objectClass: simpleSecurityObject
选用的objectClass必须有userPassword属性,userPassword属性的值为明文密码。
- 配置slapd.conf
example
authz-regexp "^uid=([^,]+).*,cn=auth$" "uid=$1,ou=system,dc=jhc,dc=cn"
- 用ldapsearch测试
example
ldapsearch -U authenticate '(uid=XXXX)'
这种方法会让密码在LDAP服务器中以明文方式存在,必须设定ACL,不允许anonymous用户对userPassword属性有read权限,但必须授予auth权限,否则会导致SASL认证无法找到用户密码。
example
access to dn.subtree="ou=system,dc=jhc,dc=cn" attrs=userPassword
by anonymous auth
by self write
by * none
- Pass-Through authentication
这里讨论的是用SASL存放密码的方法,不同于用SASL认证LDAP用户(配置DIGEST-MD5用于认证用户)。密码存放的形式可能会是下面的样子:
example
userPassword: {SASL}username@realm
这样用户的密码就可以不在LDAP服务器中,或是在另一个LDAP服务器上,认证时通过saslauthd进行认证用户。
<h3 id="Authorization">Authorization</h3>
确保通过认证的用户只能访问目录服务中指定范围的信息。OpenLDAP中主要通过ACL来控制。
- ACL语法
example
access to [resources]
by [who] [type of access granted] [control]
by [who] [type of access granted] [control]
- 哪一条ACL规则生效?
ACL可以是全局的,也可是局部的(backend内部)。针对具体请求,生效的ACL规则按如下方式确定:
- 局部规则先于全局规则。
- 局部范围里(backend内部)若找不到匹配的局部规则,就会使用全局规则。
- 不在任何backend中的记录(如Root
DSE或cn=subschema),只有全局规则有效。
- ACL规则会自上而下进行解析,直到碰到stop或整个ACL列表的结束。
- Access to \[resources\]
- access using DN
example
access to dn="uid=matt,ou=Users,dc=example,dc=com" by * none
- dn.base, dn.exact, dn.baselevel 限定对特定DN的访问。
- dn.one, dn.onelevel 当前DN下一级的entry。
- dn.sub, dn.subtree 当前DN所在子树。
- dn.children 与dn.subtree不同之处在于dn.children不包含自身。
- dn.regex
example
access to dn.regex="uid=[^,]+,ou=Users,dc=example,dc=com" by * none
- access using attrs
example
access to attrs=homePhone,homePostalAddress by * none
organizationalPerson类的所有属性不可访问(包括从person继承而来的属性)
example
access to attrs=@organizationalPerson by * none
organizationalPerson类之外的属性不可访问
example
access to attrs=!organizationalPerson by * none
禁止访问值为Matt的givenName属性
example
access to attrs=givenName val="Matt" by * none
当使用val时,attrs列出的属性只能有一个,val还可使用类似于dn的限定符:regex,subtree,base,one,exact,children。
example
access to attrs=givenName val.regex="M.*" by * none
如果属性值为DN(如groupOfNames对象的member属性),这时就可使用subtree,base,one,exact,children,regex之类限定符。
example
access to attrs=member val.children="ou=Users,dc=example,dc=com" by * none
- access using filter
example
access to filter="(|(|(givenName=Matt)(givenName=Barbara))(sn=Kant))" by * none
- combining access specifiers
组合次序如下:
example
access to [dn] [filter] [attrs] [val]
例如:
example
access to dn.subtree="ou=Users,dc=example,dc=com" filter="(employeeNumber=*)" by * none
- By \[who\] \[type of access granted\] \[control\]
by \* none
表示任何人都不可访问,唯一的例外是目录管理员(cn=Manager,dc=example,dc=com)。
- The Access Field
- w: Writes access to a record or attribute.
- r: Reads access to a record or attribute.
- s: Searches access to a record or attribute.
- c: Accesses to run a comparison operation on a record or
attribute.
- x: Accesses to perform a server-side authentication operation on a
record or attribute.
- d: Accesses to information about whether or not a record or
attribute exists ('d' stands for 'disclose').
- 0: Does not allow access to the record or attribute. This is
equivalent to -wrscxd.
| Keyword | Privileges |
|----------|------------|
| none | 0 |
| disclose | d |
| auth | xd |
| compare | cxd |
| search | scxd |
| read | rscxd |
| write | wrscxd |
例子,假定有以下限定:
example
access to attrs=givenName by * =c
此时givenName属性可在服务器端比对,但客户端运行下面的搜索时将不会显示存在givenName属性:
example
ldapsearch -LLL -U matt "(uid=matt)" givenName
使用ldapcompare却会返回TRUE:
example
dapcompare -U matt uid=matt,ou=Users,dc=example,dc=com "givenName: Matt"
再把限定改成下面的语句:
example
access to attrs=givenName by * =rcd
这时,不仅客户端能够看到givenName属性,服务器端也能对该属性进行比较。
当后一条ACL需要修改继承而来的访问权限时,应当使用"+"和"-"号。
- The who Field
- anonymous
很多客户端在认证的过程中只要用户输入用户名和密码,无从知道DN,此时就需要用anonymous绑定到服务器,找到用户名所在DN,然后进行第二次绑定。为允许这种类型的应用来使用LDAP服务器,需要让anonymous用户具有auth权限(或search权限?)。
example
access to attrs=userPassword by anonymous auth
一般ACL都会以by \* none结束,这样没有明确定义过的权限就不会授予。
- self DN对自身代表的客户。
- users 指任何通过认证的客户。这就排除了anonymous客户。
- dn 这里dn的用法与access to中dn的用法一致。
example
access to dn.subtree="ou=System,dc=example,dc=com" attrs=description
by dn="uid=barbara,ou=Users,dc=example,dc=com" write
by dn.children="ou=System,dc=example,dc=com" read
by dn.regex="uid=[^,]+,ou=Users,dc=example,dc=com" read
- groups and members 参看Mastering OpenLDAP第212-214页。
- The Control Field
control
field只有三个取值:stop,break,continue,默认为stop。stop指找到第一条匹配ACL规则(by语句)后即停止往下匹配。break会跳过当前则下面的by规则。
- 测试ACL
- ldapsearch,ldapcompare
- 使用acl或trace调试级别日志
example
slapd -d "acl,trace"
- slapacl
测试matt用户有没有读取cn=LDAP
Admins,ou=Groups,dc=example,dc=com记录的权限:
example
slapacl -U matt -b "cn=LDAP Admins,ou=Groups,dc=example,dc=com" "description/read"
slapacl -U matt -b "uid=matt,ou=Users,dc=example,dc=com" -d trace "uid/compare"
<h2 id="Tools">Tools</h2>
slap
开头的工具均为本地工具,是直接操作后台数据库的工具。ldap开头的工具则是能过LDAP协议来操作目录服务器的。前者运行更快,而后者更加灵活、通用。slapadd,slapcat,slapindex(当slapd.conf文件中的index更改了以后,就要调用slapindex更新后台索引)。
<h3 id="top-level node初始化示例如下:">top-level node初始化示例如下:</h3>
\## Build the root node. dn: dc=plainjoe,dc=org dc: plainjoe
objectClass: dcObject objectClass: organizationalUnit ou: PlainJoe Dot
Org
\## Build the people ou. dn: ou=people,dc=plainjoe,dc=org ou: people
objectClass: organizationalUnit
<h3 id="日志分析工具">日志分析工具</h3>
<http://prefetch.net/code/ldap-stats.pl.html>
<h2 id="change records ldif">change records ldif</h2>
changetype指的是对entry的操作,如changetype:
add指的即是添加一个entry。changetype:
modify指修改entry,修改entry又分多种,如add:
homeDirectory指的是为该entry添加属性和值。同样的add,含义是不同的。
openldap2.3和2.4对change records ldif的支持是不同的。
example
dn: uid=201031010730127,ou=info,ou=students,ou=people,dc=jhc,dc=cn
changetype: modify
replace: homeDirectory
homeDirectory: /home/201031010730127
add: loginShell
loginShell: /bin/bash
这种写法在2.4版本中可用,但在2.3版本中不可用。在2.3版本中必须改与如下形式:
example
dn: uid=201031010730127,ou=info,ou=students,ou=people,dc=jhc,dc=cn
changetype: modify
replace: homeDirectory
homeDirectory: /home/201031010730127
dn: uid=201031010730127,ou=info,ou=students,ou=people,dc=jhc,dc=cn
changetype: modify
add: loginShell
loginShell: /bin/bash
<h2 id="newsuperior">newsuperior</h2>
newsuperior是在LDAP3中加入的,用于移动LDAP Entry。
example
dn: uid=bjensen,ou=people,dc=example,dc=com
changetype: moddn
newsuperior: ou=employees,dc=example,dc=com
perl的Net::LDAP在处理时,moddn中只指定newsuperior时会提示没有指定newrdn错误,此时需要添加newrdn参数。
<h2 id="Replication">Replication</h2>
provider/consumer模式,consumer接收到的更新可以传播给另一个consumer,此时,consumer又成为了provider角色。同时,consumer可以是ldap客户程序,而不需要是ldap服务器。
<h3 id="sysnrepl(The LDAP Sync Replication engine)">sysnrepl(The LDAP Sync Replication engine)</h3>
sysnrepl运行于consumer一侧,可让consumer服务器保留某DIT片断的备份。它使用LDAP
Content Synchronization protocol,简称LDAP Sync。
更新也可是pull-based,也可以是push-based。pull-based意味着consumer每隔片刻就要查询provider的更新。push-based意味着consumer要随时监听provider的实时更新。Sync协议没有规定使用history
store。Syncrepl是通过管理和交换synchronization
cookies来跟踪复制的状态的。
<h3 id="refreshOnly and refreshAndPersist">refreshOnly and refreshAndPersist</h3>
<h3 id="contextCSN">contextCSN</h3>
下面的overlay语句必须加到数据库的配置之后,其它数据库配置之前:
example
overlay syncprov
它会为原数据库添加contextCSN属性,以后数据库的每次写入都会更新内存中contextCSN属性,为了让内存中的contextCSN属性与数据库中的属性同步,需要设置checkpoint
example
syncprov-checkpoint 100 10
表示每隔10分钟或100次写入操作,就要进行同步。 查询contextCSN
example
ldapsearch -x -h
<h3 id="查看服务器是否支持syncprov">查看服务器是否支持syncprov</h3>
通过下面的查询可以查出RootDSE的supportedControl等特性:
example
ldapsearch -x -LLL -b '' -s base -W -D 'cn=Manager,dc=example,dc=com' '+'
syncprov相关的输出如下:
example
supportedControl: 1.3.6.1.4.1.4203.1.9.1.1
通过http://www.alvestrand.no/objectid/1.3.6.1.4.1.4203.1.9.1.1.html
,可以查到 这串数字代表“LDAP Content Synchronization
Control”,是OpenLDAP在IANA组织登记的。其中4203即为OpenLDAP。
<h3 id="碰到的几个问题">碰到的几个问题</h3>
- 由于初始容器设置不正确,导致proxy在push时无法updateCookie,
example
syncrepl_updateCookie: rid=077 be_modify failed (32)
同时在slave端出现如下错误信息:
example
conn=1005 op=52494 MOD dn="dc=jhc,dc=cn"
conn=1005 op=52494 MOD attr=contextCSN
conn=1005 op=52494 RESULT tag=103 err=32 text=
查RFC4511可以知道:32的意思是noSuchObject,应该是slave端的容器对象不存在,无法写入contextCSN属性。由此proxy端无法更新cookie。解决方法就是重新清理slave端的数据库。
- slave端不可使用overlay syncprov slave端只用于接受更新,使用overlay
syncprov后,会导致自身添加属性,在proxy端会出现如下信息:
example
do_syncrep2: rid=077 LDAP_RES_SEARCH_RESULT
do_syncrep2: rid=077 LDAP_RES_SEARCH_RESULT (53) Server is unwilling to perform
do_syncrep2: rid=077 (53) Server is unwilling to perform
slave端出现了类似下面的查询:
example
SRCH base="dc=jhc,dc=cn" scope=2 deref=0 filter="(entryCSN>=20150412132055.766293Z#000000#000#000000)"
- slave端的updatedn可以设置成rootdn
slapd.conf文件中说updatedn不应该设置成master中的rootdn。我不能确切知道这句话的意思。master与slave一般不是同一机器,updatedn应该是根据slave自身的配置来设置,与master似乎没有关系。因此可以设置成slave中的rootdn。
也可以在slave中添加专门用于replicator写入的帐号。
- size和time要设置为unlimited
初次同步,查询的数量和时间都很大,不设置为unlimited,会出size超限错误。master中用于查询的帐号:
example
limits dn.exact="uid=authenticate,ou=system,dc=jhc,dc=cn" time.soft=unlimited time.hard=unlimited size.soft=unlimited size.hard=unlimited
<h2 id="备份与恢复">备份与恢复</h2>
在原始服务器端以超级用户执行下面语句进行备份:
example
slapcat -l info.ldif
在备份服务器端用下面语句恢复数据:
example
sudo slapadd -b 'dc=jhc,dc=cn' -l info.ldif
需要注意两点:
- 在恢复命令执行之前必须停止slapd进程,否则会出现"database already in
use"错误。
- 用sudo执行slapadd命令后,会把hdb数据库的相关文件(objectClass.bdb)的所有者改为root,导致直接启动slapd时出现bdb恢复之类的错误,重启slapd前务必把权限修改好。
<h2 id="Troubleshooting">Troubleshooting</h2>
<h3 id="配置monitor数据库">配置monitor数据库</h3>
未配置monitor数据库时,slapcat出现下列提示
example
[root@cent2 ~]# slapcat -l info.ldif
bdb_monitor_db_open: monitoring disabled; configure monitor database to enable
<h3 id="Ubuntu默认未加载back<sub>bdb模块</sub>">Ubuntu默认未加载back<sub>bdb模块</sub></h3>
此时在slapd.conf中使用 database bdb会导致下面所示错误:
example
解决办法是在slapd.conf中添加:
example
moduleload back_bdb.la
<h3 id="postfix发出的奇怪的查询">postfix发出的奇怪的查询</h3>
conn=1001 fd=19 ACCEPT from IP=10.2.3.77:9074 (IP=0.0.0.0:389)
conn=1001 op=0 BIND dn="" method=128
conn=1001 op=0 RESULT tag=97 err=0 text=
conn=1001 op=1 SRCH base="ou=people,dc=jhc,dc=cn" scope=2 deref=0 filter="(&(objectClass=posixAccount)(uid=postfix))"
conn=1001 op=1 SEARCH RESULT tag=101 err=0 nentries=0 text=
conn=1001 op=2 SRCH base="ou=groups,dc=jhc,dc=cn" scope=2 deref=0 filter="(&(objectClass=posixGroup)(memberUid=postfix))"
conn=1001 op=2 SRCH attr=gidNumber
conn=1001 op=2 SEARCH RESULT tag=101 err=0 nentries=0 text=
<h3 id="Subversion没有配置好导致发出匿名查询">Subversion没有配置好导致发出匿名查询</h3>
conn=6377 fd=20 ACCEPT from IP=10.2.3.77:62398 (IP=0.0.0.0:389)
conn=6377 op=0 BIND dn="" method=128
conn=6377 op=0 RESULT tag=97 err=0 text=
conn=6377 op=1 SRCH base="ou=people,dc=jhc,dc=cn" scope=2 deref=0 filter="(&(objectClass=posixAccount)(uid=201031010730116))"
conn=6377 op=1 SRCH attr=uid userPassword uidNumber gidNumber cn homeDirectory loginShell gecos description objectClass
conn=6377 op=1 SEARCH RESULT tag=101 err=50 nentries=0 text=
conn=6377 op=2 UNBIND
conn=6377 fd=20 closed
<h3 id="null<sub>callback</sub>">null<sub>callback</sub></h3>
配置push模式的syncprov时,经常碰到null<sub>callback错误</sub>。
example
null_callback : error code 0x31
我是在目标LDAP服务器上的帐号权限不对情况下,看到这个错误信息的。
example
null_callback : error code 0x34
examplecat /var/run/syslogd.pid\后来我发现ssh转发端口关掉了,导致这个错误,估计是与目标服务器连接出错导致。
<h1 id="syslog <span class="tag" tag-name="syslog"><span class="smallcaps">syslog</span></span>">syslog <span class="tag" tag-name="syslog"><span class="smallcaps">syslog</span></span></h1>
<h2 id="syslog的结构">syslog的结构</h2>
<h3 id="syslogd守护进程及配置文件/etc/syslog.conf">syslogd守护进程及配置文件/etc/syslog.conf</h3>
应用程序把log条目写入/dev/log(UNIX domain
socket),syslogd读取该文件,通过查询配置文件,把相关的信息发送到相应的目的地。HUP信息可让syslogd关闭日志文件,重新读取配置文件。一般情况下syslogd会把自己的进程id写到/var/run/syslogd.pid中,因此发送HUP信息的script语句如下:
kill -HUP \<h3 id="/etc/syslog.conf">/etc/syslog.conf</h3>
- 基本格式
selector \<Tab\> action
值得注意的是,中间的分隔符必需使用tab,很多版本不支持使用空格来分隔,复制粘贴有可能会导致问题。
- selector的语法
facility.level
其中facility用于定位发送日志信息的应用程序,由kernel定义,其余的程序只能使用“user.”。
两个通配符: \* - all, none - nothing
多个facility可用逗号分隔,多个selector可用分号分隔。通常用分号分隔的selector之间的逻辑关系是“或”,但如果某个selector在level中使用了none,则同一行的其余selector不会对none修饰的facility起作用。
- facility名字
| Facility | Programs that use it |
|----------|----------------------------------------------|
| \* | All facilities except "mark" |
| auth | Secirity and authorization-related commands |
| authpriv | Sensitive/private authorization messages |
| cron | The cron daemon |
| daemon | System daemons |
| ftp | The FTP daemon,ftpd |
| kern | The kernel |
| local0-7 | Eight flavors of local message |
| lpr | The line printer spooling system |
| mail | sendmail and other mail-related software |
| mark | Time stamps generated at regular intervals |
| news | The Usenet news system(obsolete) |
| syslog | syslogd internal messages |
| user | User processes(the default if not specified) |
| uucp | Obsolete,ignore |
- syslog severity level
| Level | Approximate meaning |
|---------|---------------------------------------|
| emerg | Panic situations |
| alert | Urgent situations |
| crit | Critical conditions |
| err | Other error conditions |
| warning | Warning messages |
| notice | Things that might merit investigation |
| info | Informational messages |
| debug | For debugging only |level指定了日志消息的重要性,标记为warning的邮件日志会和以下的所有selector匹配:
mail.warning,mail.info,mail.notice,mail.debug,\*.warning,\*.notice,\*.info,\*.debug
Linux版本的syslog还允许使用=(只用于本级别)和!(除了本级别以及更高的级别),例子如下:| Selector | Meaning |
|---------------------------|---------------------------------------------------|
| mail.info | Mail-related messages of info priority and higher |
| mail.=info | Only messages at info priority |
| mail.info;mail.!err | Only priorities info,notice,and warning |
| mail.debug;mail.!=warning | All priorities except warning |
- Syslog actions
| Action | Meaning |
|--------------|------------------------------------------------------------------|
| filename | Appends the message to a file on the local machine |
| @hostname | Forwards the message to the syslogd on hostname |
| @ipaddress | Forwards the message to the syslod on host ipaddress |
| 竖fifoname | Writes the message to the named pipe fifoname |
| user1,user2… | Writes the message to the screens of users if they are logged in |
| \* | Writes the message to all users who logged in |Linux系统中可以在filename的前面添加-(dash),用于关闭log
entry写入与文件的同步(sync)。sync可以让系统在崩溃时尽可能多地保留日志信息,但对一个繁忙的系统来说可能会导致性能问题,因此大部时候,Linux系统中的action前面都应该有dash。<h3 id="openlog,向syslogd提交信息的运行时库">openlog,向syslogd提交信息的运行时库</h3>
<h3 id="logger,从shell发送log entry的命令">logger,从shell发送log entry的命令</h3>
logger命令可用于测试syslogd的配置。假如syslogd.conf中有以下配置
local5.warning /tmp/evi.log 可用以下命令测试配置文件的正确性 logger -p
local5.warning "test message"<h2 id="Linux kernel and boot-time logging">Linux kernel and boot-time logging</h2>
kernel的log必须不依赖于任何文件系统,解决的方案是分配固定的内存来存放kernel的log信息,用dmesg命令来提取。klogd守护进程提供了比dmesg更高级的功能,它能够从kernel的log缓存中读取信息,再转发到syslogd。syslog配置文件中的kern
facility即是。 通过以下命令,可以设置kernel的console logging level
ubuntu\$ sudo dmesg -n 2 以下两个kernel参数与log配置有关
/proc/sys/kernel/printk<sub>ratelimit</sub>
/proc/sys/kernel/printk<sub>ratelimitburst</sub><h2 id="logrotate">logrotate</h2>
Debian系统提供了savelog。
默认配置文件/etc/logrotate.conf,默认状态文件/var/lib/logrotate.status。<h2 id="扫描日志的工具">扫描日志的工具</h2>
swatch(可实时监控),logcheck,logwatch(报告历史数据),Simple Event
Correlator(SEC)<h2 id="注意点">注意点</h2>
- 安全相关的日志信息需要立即关注。失败的login、su、sudo信息能够很早地暴露出非法闯入的企图。如果是某人忘记密码,那就可以预先给予帮助。
- 磁盘将满的消息需要立即处理。
- 关注一些反复出现的事件。
- 设计日志策略时要留意以下几个问题
- How many systems and applications will be included?
- What type of storage infrastructure is available?
- How long must logs be retained?
- What types of events are important?
- 对大部分的应用程序都需要记录以下信息
- Username or user ID
- Event success or failure
- Source address for network events
- Date and time (from an authoritative source, such as NTP)
- Sensitive data added, altered, or removed
- Event details
<h1 id="OpenSSH <span class="tag" tag-name="ssh"><span class="smallcaps">ssh</span></span>">OpenSSH <span class="tag" tag-name="ssh"><span class="smallcaps">ssh</span></span></h1>
<h2 id="端口转发">端口转发</h2>
ssh -f -N -L12345:localhost:23 www
需要注意的是-L参数中的localhost,它指的并不是运行ssh命令的主机,而是"www"主机,localhost是相对于"www"主机而言的。如果不使用localhost,此时端口转发可称为Pass-through
Forwarding,相当于把"www"主机当作中转站,由www主机向其它服务器发送请求并送回响应。如:
example
ssh -L12345:john:389 paul
其中的paul是中转站,由paul代替了ssh客户端所在主机和john通信。
<h2 id="SSH后台转发">SSH后台转发</h2>
一个用来保持ssh连接的脚本:
shell
#! /bin/bash
set -e
while :; do
echo
sleep 60
done
authorized<sub>keys文件中可作如下配置</sub>:
example
command="/home/kakajou/idle",no-agent-forwarding,no-user-rc,no-X11-forwarding,permitopen="www-proxy:3128",permitopen="imap:143",permitopen="smtp:25" ssh-rsa AAAAB3N...Q== root@kakajou
<http://www.greenend.org.uk/rjk/sshfwd/>
<h2 id="X转发配置">X转发配置</h2>
- 服务器配置 X11Forwarding yes ForwardX11 yes AllowX11Forwarding yes
上面三个配置是相同的,只要一个即可。
- 必须安装xauth centos的包名为xorg-x11-xauth,Ubuntu的包名为xauth
- 客户端使用-X或-Y选项
<h2 id="acl对SSH认证的影响">acl对SSH认证的影响</h2>
gitlab服务器,在设置了ACL规则后,auth.log中看到下面的日志:
Authentication refused: bad ownership or modes for directory /home/git
原本想方便自己使用,让luyanfei帐号对/home/git目录有完全的读写权限,然而这么设置后,就无法用ssh
key来登录了。sshd检测到不符合要求的权限设置后,直接拒绝git用户的登录,于是用户在使用时就遇到输入git用户密码的提示。
/home/git .ssh 目录都需要删除额外的读写权限。
<h2 id="保持端口转发">保持端口转发</h2>
example
ssh -f -R9898:localhost:22 infojhc.cn ~/scripts/idle.sh
shell
#!/bin/bash
while :; do
echo ''
sleep 60
done
<h2 id="SSH文件系统(FUSE可sshfs)">SSH文件系统(FUSE可sshfs)</h2>
<h2 id="参考资源">参考资源</h2>
shell
- OpenSSH" target="_blank">https://www.linux.com/learn/tutorials/398593:openssh-tips-and-tricks-beyond-secure-shell">OpenSSH Tips and Tricks: Beyond Secure
Shell
- Howto" target="_blank">http://www.debianadmin.com/howto-use-ssh-local-and-remote-port-forwarding.html">Howto use SSH local and remote port
forwarding
<h1 id="firewall <span class="tag" tag-name="firewall"><span class="smallcaps">firewall</span></span>">firewall <span class="tag" tag-name="firewall"><span class="smallcaps">firewall</span></span></h1>
<h2 id="NAT <span class="tag" tag-name="nat"><span class="smallcaps">nat</span></span>">NAT <span class="tag" tag-name="nat"><span class="smallcaps">nat</span></span></h2>
NAT用在Client端,可隐藏Client的IP。NAT用在Server端,则可保护主机安全。如果不配置NAT,私有IP地址的主机可以成功发送数据到外网主机,然而该外网主机却无法回复该私有IP地址。
iptables在配置NAT时,只需配置网络连接的一个方面,连接跟踪机制会自动生成反向连接信息,因此响应封包会自动进行处理。
NAT分为4种:一对多,多对多,一对一及NAPT(Network Address Port
Translation)。不管是哪一咱NAT,都需要SNAT(Source NAT)和DNAT(Destination
NAT)搭配起来使用。SNAT由POSTROUTING Chain来实现,而DNAT由PREROUTING
Chain来实现。在设置POSTROUTING时,需要指定封包的流出接口,因此须用-o选项。在设置PREROUTING时,则需指定封包的流入接口,因此必须使用-i选项。一对多配置示例如下:
example
iptables -t nat -A POSTROUTING -o eth0 -s 192.168.0.0/24 -j SNAT --to 10.0.1.200
一对多的缺点是对外只有一个IP,如果某在线游戏限制一个IP只能有一个玩家,那么一对多配置方式下就只能有一个玩家玩这个游戏了。一对多的配置如下:
example
iptables -t nat -A POSTROUTING -o eth0 -s 192.168.0.0/24 -j SNAT --to 10.0.1.200-10.0.1.205
实现这种配置的前提条件是公共IP是连续的。
一对一配置要求有足够的公共IP,让公共的接口绑定多个IP,针对每个IP配置DNAT和SNAT两个方向。配置进入方向:
example
iptables -t nat -A PREROUTING -i eth0 -d 10.0.1.201 -j DNAT --to 192.168.0.1
任何对10.0.1.201的访问(来自eth0)都会转到192.168.0.1。这样配置完成后,外网IP即可正常访问10.0.1.201。如果要让192.168.0.1这台主机也以10.0.1.201访问外网资源,可配置出去方向:
example
iptables -t nat -A POSTROUTING -i eth0 -s 192.168.0.1 -j SNAT --to 10.0.1.201
NAPT配置示例:
example
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j DNAT --to 192.168.0.1:80
对80端口的访问会被转到192.168.0.1这台机器的80端口上。
<h1 id="mail <span class="tag" tag-name="mail"><span class="smallcaps">mail</span></span>">mail <span class="tag" tag-name="mail"><span class="smallcaps">mail</span></span></h1>
<h2 id="术语">术语</h2>
- CC(Carbon Copy)抄送
在使用手动机械打字机时代,采用复写纸,同样一份文件可以一次打印出两份乃至多份,分送给不同的人。到了互联网时代,如果一份邮件需要发送给多个人阅读,只要在“抄送”或“CC”一栏填上相关人员的信箱地址即可。如果是抄送多人,只要将他们的地址用逗号隔开,即可发送到相应的信箱。
- BCC(Blind Carbon Copy)密送
于某种原因,你不希望收信人知道你把这封邮件还发送给了另外的人,则可将这位幕后的人的信箱地址放在密送栏中。
- MH The RAND MH Message Handling System
<http://rand-mh.sourceforge.net/>
- SPF(Sender Policy Framework)
由RFC4408定义。用下面的命令查看DNS服务器中的SPF记录信息(实际上应该是TXT记录,SPF记录还比较新)
- dig gmail.com spf
example
gmail.com. 300 IN SPF "v=spf1 redirect=_spf.google.com"
- dig <sub>spf</sub>.google.com spf
example
_spf.google.com. 131 IN TXT "v=spf1 ip4:216.239.32.0/19 ip4:64.233.160.0/19 ip4:66.249.80.0/20 ip4:72.14.192.0/18 ip4:209.85.128.0/17 ip4:66.102.0.0/20 ip4:74.125.0.0/16 ip4:64.18.0.0/20 ip4:207.126.144.0/20 ip4:173.194.0.0/16 ?all"
v=spf1表示使用SPF1.0版本,v=spf2.0则表示Microsoft Sender ID。
接下来的网络地址代表gmail官方认可的对外的邮件服务器的网络地址,接收邮件的MTA可用此记录来验证发信方的可信度。
?all表示记录的结束。
- Sender ID 由RFC4406定义,与SPF不同的是,Sender
ID是Microsoft专有技术。
- Reject和Bounce
接收方MTA在SMTP对话中拒绝接收消息,向发送人报告error,此种情况称为Reject。接收方MTA已经接收消息,但后来发现它无法递送,向发送人报告error,此种情况称为Bounce。
- Delete & Expunge
Delete只是把邮件标记为删除,邮件本身并不会消失。Expunge则会真正删除邮件。
- unsolicited commercial email (UCE)
<h2 id="SMTP <span class="tag" tag-name="smtp"><span class="smallcaps">smtp</span></span>">SMTP <span class="tag" tag-name="smtp"><span class="smallcaps">smtp</span></span></h2>
<h2 id="mail message">mail message</h2>
- The envelope
记录收信地址和退信地址,MTA内部使用,一般用户看不到。envelope地址与headers中的from和to可以不同。
- The headers
headers就是一系列property/value,由RFC5322定义。一般的邮件客户端都会隐藏与用户关系不大的部分。
阅读headers要顺着Received从下往上读。
- The body of the message
<h2 id="mailutils">mailutils</h2>
<h3 id="movemail">movemail</h3>
用movemail可以将邮件从远程移到本地:
example
/usr/bin/movemail.mailutils -v --emacs --tls --debug-level="mailbox=proto" imap://jhc_moodle@imap.qq.com /home/luyanfei/.newmail_jhc password
<h3 id="mail">mail</h3>
example
echo "hello,this is the content of mail.welcome to www.mzone.cc" | mail -s "Hello from mzone.cc by pipe" admin@mzone.cc
mail -s "Hello from mzone.cc by file" admin@mzone.cc < mail.txt
yum install sharutils
uuencode test.txt test | mail -s "hello,see the attachement" admin@mzone.cc < mail.txt
<h2 id="postfix <span class="tag" tag-name="postfix"><span class="smallcaps">postfix</span></span>">postfix <span class="tag" tag-name="postfix"><span class="smallcaps">postfix</span></span></h2>
<h3 id="pflogsumm分析Postfix日志">pflogsumm分析Postfix日志</h3>
<h3 id="配置saslauthd的注意事项">配置saslauthd的注意事项</h3>
<http://www.postfix.org/SASL_README.html>
Ubuntu的postfix比较特殊,需要特别注意:
<https://help.ubuntu.com/community/Postfix>
- smtpd.conf要放在/etc/postfix/sasl目录下,这是Debian打包时规定好的。
- Debian的postfix使用了chroot,必须修改saslauthd的配置文件目录
/var/spool/postfix/var/run/saslauthd
需要注意的是这么做会让其它依赖saslauthd的应用受到影响。
<h3 id="用免费邮箱发送邮件时的注意事项">用免费邮箱发送邮件时的注意事项</h3>
同时开启smtp线程太多可能会被挡掉,甚至帐号被禁用,因此有必要限定线程数量
initial<sub>destinationconcurrency</sub> = 1
local<sub>destinationconcurrencylimit</sub> = 2
default<sub>destinationconcurrencylimit</sub> = 3
<h3 id="postfix的smtp客户端不支持与smtp服务器465端口连接">postfix的smtp客户端不支持与smtp服务器465端口连接</h3>
如果让postfix选择外部邮件服务器来转发邮件,一定要注意不使用465端口。官方网站的说明如下:
The Postfix SMTP client does not support the obsolete "wrappermode"
protocol, which uses TCP port 465 on the SMTP server.
如果去连465号端口会得到下面的日志:
example
CLIENT wrappermode (port smtps/465) is unimplemented
instead, send to (port submission/587) with STARTTLS
说明连接会转到587号端口,服务器上这个端口没有提供对应服务的话,那连接只能time
out。
gmail提供了465和587两个端口,在transport中直接设置587号端口可以正常发送邮件。smtp.163.com也有两个:465和994,但994号端口我测试时没有成功。
<h3 id="Postfix中设置使用gmail来转发邮件">Postfix中设置使用gmail来转发邮件</h3>
# smtp SASL
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/gmail/gmail_sasl_passwd
smtp_sasl_type = cyrus
smtp_sasl_security_options = noanonymous
# smtp ssl
smtp_tls_security_level = fingerprint
# 用openssl s_client x509两个命令可获取smtp.google.com证书指纹
smtp_tls_fingerprint_cert_match = F3:92:AE:B4:28:FE:64:03:6F:E1:55:ED:71:9E:5F:F6:88:90:5A:57
smtp_tls_fingerprint_digest = sha1
smtp_sasl_tls_security_options = noanonymous
tls_random_source = dev:/dev/urandom
smtp_tls_key_file = /etc/postfix/gmail/gmail.key
smtp_tls_cert_file = /etc/postfix/gmail/gmail.crt
smtp_tls_loglevel = 1
# smtp thread
initial_destination_concurrency = 2
local_destination_concurrency_limit = 2
default_destination_concurrency_limit = 3
transport_maps = hash:/etc/postfix/gmail/transport
sender_canonical_maps = hash:/etc/postfix/gmail/sender_canonical
<h3 id="Postfix发件的频率控制">Postfix发件的频率控制</h3>
postfix发信的频率控制有几个:anvil<sub>ratetimeunit</sub> = 120s
设置限制时间
smtpd<sub>clientconnectionratelimit</sub>=5 ip地址连接的频率控制.
含义为在120s内发送5封邮件 smtpd<sub>clientmessageratelimit</sub>=5
客户端发送邮件的频率控制.(某种情况下smtpd<sub>clientmessageratelimit和</sub>
smtpd<sub>clientconnectionratelimit的效果是一样</sub>.smtpd<sub>clientconnectionratelimit是指连接到服务器25端口就开始计算</sub>,smtpd<sub>clientmessageratelimit是发送邮件时计算</sub>.)
smtpd<sub>clientrecipientratelimit</sub>=10 rcpt
to的频率控制,也就是说TO了多少个人.
smtpd<sub>clienteventlimitexceptions</sub> =
\${smtpd<sub>clientconnectionlimitexceptions</sub>:\$mynetworks}
指不做频率控制检测的网络,默认是mynetworks.
posftfix的性能控制
之所以对postfix的性能进行控制,是为了在遇到邮件风暴时保证postfix可以正常运行。通常,我们可以通过对下列postfix参数的配置来调节postfix的性能,这些参数都是通过mail.cf配置文件进行配置的,修改以后不要忘了运行postfix
reload命令来使配置生效。
1. 进程数限制 可以通过default<sub>processlimit</sub>
参数来控制postfix系统同时可以运行的最大进程数目。缺省值是50个。
2. 对同一目标主机的并发连接限制
当向同一目标主机发出SMTP连接时,postfix初始化发出两个SMTP连接,如果投递成功则增加并发的SMTP连接数目,遇到拥塞时又减少并发连接的数目。postfix中通过以下的参数对同一目标主机的并发连接进行控制:
- initial<sub>destinationconcurrency</sub>:控制对同一目标主机的初始化并发连接数目。缺省值为2。
- default<sub>destinationconcurrencylimit</sub>:控制初始化连接后对同一目标主机的最大并发连接数目。缺省值为10。
- local<sub>destinationconcurrencylimit</sub>:控制对同一本地收件人的最大同时投递的邮件数目。缺省值为2,因为对本地同一收件人投递邮件时投递工作只能一个接一个的进行,所以设得在大也没用。
3. 对同一封邮件的收件人数目限制
通过default<sub>destinationrecipientlimit参数来控制postfix的投递代理</sub>(如
smtp进程)可以将同一封邮件发送给多少个收件人。缺省值为50。也可以用明确指出该投递代理的参数来覆盖该缺省值。如用smtpd<sub>recipientlimit来指定smtp投递代理可以将同一封邮件发送给多少个收件人</sub>,该参数的缺省值为1000。
4. 推迟投递控制
通过defer<sub>transports参数</sub>,我们可以推迟投递该参数指定的邮件直到postfix明确的提出投递要求。下面我们看一个例子:
有一个小型的局域网,用户都将邮件发送给局域网内部的一台postfix
邮件服务器,然后通过在该服务器上拨号将邮件发送出去。这时我们可以这样指定该参数的值:
defer<sub>transports</sub> = smtp
该语句表示postfix推迟投递所有的邮件直到执行sendmail -q命令,这样
我们就可以在ppp的脚本中加上sendmail
-q,以便在拨号成功后让postfix开始投递邮件。
5. 关于延迟邮件的再投递控制
可以通过以下的几个参数实现对延迟邮件的再投递控制:
queue<sub>rundelay</sub>:设置队列管理进行扫描deferred邮件队列的频率,缺省值为1000秒。
maximal<sub>queuelifetime</sub>:设置postfix在放弃投递而返回不可投递信息前,被延迟邮件再deferred邮件队列中的生存时间。
minimal<sub>backofftime</sub>:当一封邮件投递失败后,邮件队列将在一段时间内忽视该邮件的存在,也就是我们前面讲的时间邮票。该参数就是用来设置最小的时间邮票。缺省值为1000秒。
maximal<sub>backofftime</sub>:设置最大的时间邮票。
6. 对拒绝服务攻击的处理
postfix对每一个SMTP会话都设置一个错误计数器,当该客户端的请求未
被接受或违反那UCE规则时,该计数器就增1。随着计数器的增加,postfix将采取不同的措施来防止恶意用户的拒绝服务攻击。
smtpd<sub>errorsleeptime</sub>:当该错误计数器的值还很小时,postfix将暂停
smtpd<sub>errorsleeptime指定的时间</sub>,然后向客户端报告一个错误。该参数的缺省值为5秒。
smtpd<sub>softerrorlimit</sub>:当错误计数器的值超过该参数指定的值时,postfix在响应该客户端请求前将沉睡一段时间。缺省值为10。
smtpd<sub>harderrorlimit</sub>:当错误计数器的值超过该参数指定的值时,postfix
中断同该客户端的连接。缺省值为100。 postfix对使用资源的控制
通过特定的postfix配置参数,我们可以实现postfix运行时对所消耗的资源的灵活控制。可以通过以下几个方面来控制postfix消耗的资源:
1. 限制内存中的对象的大小
要控制对内存资源的消耗,必须控制内存中对象的大小。可以用以下的参数来进行对象大小的控制:
line<sub>lengthlimit</sub>:控制读入数据时每一行的大小,如果太长则强行将其分割成更短的行,太长的行在投递时再重组。缺省值为2048
bytes。 header<sub>sizelimit</sub>:限制信头长度。缺省值为102400bytes。
message<sub>sizelimit</sub>:限制postfix队列文件的大小。缺省值为10240000
bytes。
queue<sub>minfree</sub>:邮件队列中可用的空间大小。缺省为无限制。建议该值最好时message<sub>sizelimit的数倍以便于处理大邮件</sub>。
bounce<sub>sizelimit</sub>:限制某一邮件不可投递时,返回给发件人不可投递报告的大小,缺省值为50000
bytes。
2. 限制内存中对象的数目
qmgr<sub>messagerecipientlimit</sub>:设置内存中收件人地址的最大数目。缺省值为10000。
qmgr<sub>messageactivelimit</sub>:设置active邮件队列中邮件数目的最大值。缺省值为1000。
duplicate<sub>filterlimit</sub>:设置需要local和cleanup后台程序记住的收件人地址的最大数目。缺省值为1000。
3.限制等待一个外部命令完成的时间
command<sub>timelimit</sub>:设置local程序等待一个外部命令完成的时间。缺省值为1000秒。
4. 限制文件锁定的操作时间
deliver<sub>lockattempts</sub>:设置锁定一个文件的最大尝试次数。缺省值为5次。
deliver<sub>lockdelay</sub>:设置如果锁定一个文件失败后再次尝试的等待时间,缺省值为1秒。
5. 控制错误恢复
在某些情况下(如高负载),postfix的某个进程可能会死掉,这时master进
程会试图重新启动该进程,我们可以通过下面的参数来控制这种行为:
fork<sub>attempts</sub>:试图重启动一个进程的最大尝试次数。缺省值为5次。
fork<sub>delay</sub>:每两次尝试之间的等待时间,缺省值为1秒。
transport<sub>retrytime</sub>:队列管理进程每两次尝试连接一个不正常的投递代理进程之间的等待时间。缺省为60秒。
<h3 id="Filtering spam with Postfix">Filtering spam with Postfix</h3>
<http://www.freesoftwaremagazine.com/articles/focus_spam_postfix/>
<h2 id="设置DNS的SPF记录">设置DNS的SPF记录</h2>
SPF的完整意思为 "Sender Policy
Framework"。翻译过来就是发送方策略框架,是一项跟 DNS
相关的技术,它的内容写在 DNS 的 txt 类型记录里面,关于更详细的信息请参考
RFC4408: <http://www.ietf.org/rfc/rfc4408.txt> SPF
的原理是使用电子邮件的头部信息中的 'Return Path' 或 'Mail From'
这两个邮件头里的域名来结合真正提供这个邮件的服务商 DNS
里面的记录去验证发送邮件服务器是否是冒充行为。SPF 的官方网站是:
<http://www.openspf.org/> 当信件发送到一个 MTA 后,这个 MTA
会检查邮件的邮件头,然后进行 DNS 查询,如果认为这个域的 IP
地址不是所定义的那就证明他是一个假冒的,这时候就会把邮件退回去。 How to
check SPF record
如何查询某个域名是否开启SPF,可以使用以下命令:
只需要去查询 TXT 类型的 DNS 记录即可。 SPF variables
v=spf1 / 表示 spf1 的版本
- Fail, 表示没有其他任何匹配发生
~ 代表软失败,通常用于测试中 ? 代表忽略 IP4 代表IPv4进行验证 IP6
代表IPv6进行验证 all 代表结束
举例说明:
- 163 的 SPF 记录:
163.com. 18000 IN TXT "v=spf1 ip4:220.181.12.0/22 ip4:202.108.5.64/26
ip4:202.108.5.128/25 -all"
如果不符合以上发信地址,但是发件人却是
'xxx@163.com',那么这样的邮件一定是假冒的。邮件应该一律退回。 sohu.com
的 SPF 记录: sohu.com. 600 IN TXT "v=spf1 ip4:61.135.130.0/23
ip4:61.135.132.0/23 ip4:61.135.134.0/23 ip4:61.135.145.0/23
ip4:61.135.150.0/23 ip4:220.181.26.0/24 ip4:222.28.152.128/25
ip4:218.206.87.0/25 ip4:221.236.12.128 ip4:203.184.141.0/24
ip4:61.152.234.0/24 ~all"
<h1 id="systemd <span class="tag" tag-name="systemd"><span class="smallcaps">systemd</span></span>">systemd <span class="tag" tag-name="systemd"><span class="smallcaps">systemd</span></span></h1>
<h2 id="限制journal的容量">限制journal的容量</h2>
example
journalctl --vacuum-time=3months
journalctl --vacuum-size=500M
<h2 id="Personal Package Archive location">Personal Package Archive location</h2>
systemd and related packages are available on this PPA To use the PPA,
first add it to your software sources list as follows.
example
add-apt-repository ppa:pitti/systemd
apt-get update
<h2 id="Kernel requirements">Kernel requirements</h2>
General setup —\> \[\*\] Control Group support Device Drivers —\>
Generic Driver Options —\> \[\*\] Maintain a devtmpfs filesystem to
mount at /dev \[\*\] Automount devtmpfs at /dev, after the kernel
mounted the rootfs (NEW) File systems —\> \< \> Kernel automounter
support \<\*\> Kernel automounter version 4 support (also supports v3)
<h2 id="Installing systemd">Installing systemd</h2>
example
apt-get install systemd libpam-systemd systemd-gui systemd-extra-units
<h2 id="Boot loader configuration">Boot loader configuration</h2>
After installation, the machine will still boot under upstart by
default. To boot under systemd, the following argument must be specified
on the kernel command line:
example
init=/lib/systemd/systemd
Note that the systemd binary resides now in *lib/systemd* and
/bin/systemd is just a symlink to it. To boot under systemd by default,
edit /etc/default/grub and change the following line:
example
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash init=/lib/systemd/systemd"
After modifying any grub related configuration files like
/etc/default/grub the following command is needed to bring the changes
into effect.
example
update-grub
<h2 id="/etc/mtab">/etc/mtab</h2>
systemd prints the following warning on boot:
example
/etc/mtab is not a symlink or not pointing to /proc/self/mounts. This is not supported anymore. Please make sure to replace this file by a symlink to avoid incorrect or misleading mount(8) output.
It is advisable to do as suggested and replace /etc/mtab. It is not only
mount that will behave incorrectly otherwise, but also df and probably
most other commands that look at the list of mounted filesystems. This
change can be made as follows.
example
ln -fs /proc/self/mounts /etc/mtab
<https://wiki.ubuntu.com/systemd>
<h2 id="参考资源">参考资源</h2>
<http://freedesktop.org/wiki/Software/systemd/>
<https://wiki.archlinux.org/index.php/systemd_(简体中文)>
<h1 id="alfresco">alfresco</h1>
<http://wiki.alfresco.com/wiki/Alfresco_With_mod_auth_cas>
<h1 id="cron">cron</h1>
<h2 id="错误处理">错误处理</h2>
postfix服务最好不关,因为cron执行脚本的信息会通过mail命令发送邮件给用户,没有邮件服务器投递,这些信息就看不到了。
如果要查看cron脚本的错误信息输出,那要注意在命令中对stderr重定向:
example
0 0 * * * (/usr/bin/time /path/to/mysqlbackup.sh 2&>1) | /bin/mail -s "MySQL Backup" "admin@example.com"
<h2 id="关闭crontab的邮件通知">关闭crontab的邮件通知</h2>
- 设置MAILTO
example
MAILTO=""
- 输出重定向
example
0 1 5 10 * /path/to/script.sh >/dev/null 2>&1
<h2 id="cron脚本中使用sudo注意事项">cron脚本中使用sudo注意事项</h2>
sudo默认配置需要tty,直接在cron脚本中使用sudo会导致下面的错误:
example
sudo: sorry, you must have a tty to run sudo
解决办法有两种,一是修改/etc/sudoers,将下面这一行注释掉:
example
Defaults requiretty
bash。二是使用的命令中添加伪终端,ssh命令可加-t参数,其它命令考虑使用expect或script命令。
<h2 id="PATH变量问题">PATH变量问题</h2>
1.crontab与环境变量
不要假定cron知道所需要的特殊环境,它其实并不知道。所以你要保证在shelll脚本中提供所有必要的路径和环境变量,除了一些自动设置的全局变量。所以注意如下3点:1)脚本中涉及文件路径时写全局路径;
2)脚本执行要用到java或其他环境变量时,通过source命令引入环境变量,如:
cat start<sub>cbp</sub>.sh \#!/bin/sh source /etc/profile export
RUN<sub>CONF</sub>=/home/d139/conf/platform/cbp/cbp<sub>jboss</sub>.conf
/usr/local/jboss-4.0.5/bin/run.sh -c mev &
3)当手动执行脚本OK,但是crontab死活不执行时。这时必须大胆怀疑是环境变量惹的祸,并可以尝试在crontab中直接引入环境变量解决问题。如:
0 \* \* \* \* . /etc/profile:/bin/sh
/var/www/java/audit<sub>nocount</sub>/bin/restart<sub>audit</sub>.sh2.其他应该注意的问题 1)新创建的cron
job,不会马上执行,至少要过2分钟才执行。如果重启cron则马上执行。 2)每条
JOB
执行完毕之后,系统会自动将输出发送邮件给当前系统用户。日积月累,非常的多,甚至会撑爆整个系统。所以每条
JOB 命令后面进行重定向处理是非常必要的: \>/dev/null 2\>&1 。前提是对
Job 中的命令需要正常输出已经作了一定的处理, 比如追加到某个特定日志文件。
3)当crontab突然失效时,可以尝试/etc/init.d/crond
restart解决问题。或者查看日志看某个job有没有执行/报错tail -f
/var/log/cron。 4)千万别乱运行crontab
-r。它从Crontab目录(/var/spool/cron)中删除用户的Crontab文件。删除了该用户的所有crontab都没了。
5)在crontab中%是有特殊含义的,表示换行的意思。如果要用的话必须进行转义\\,如经常用的date
‘+%Y%m%d’在crontab里是不会执行的,应该换成date ‘+\\Y\\m\\d’\3.crontab中的输出配置
crontab中经常配置运行脚本输出为:\>/dev/null
2\>&1,来避免crontab运行中有内容输出。shell命令的结果可以通过‘\> ’的形式来定义输出
/dev/null 代表空设备文件
\> 代表重定向到哪里,例如:echo "123" \> /home/123.txt
1
表示stdout标准输出,系统默认值是1,所以"\>/dev/null"等同于"1\>/dev/null"2 表示stderr标准错误
& 表示等同于的意思,2\>&1,表示2的输出重定向等同于1
那么重定向输出语句的含义: 1\>/dev/null
首先表示标准输出重定向到空设备文件,也就是不输出任何信息到终端,不显示任何信息。
2\>&1
表示标准错误输出重定向等同于标准输出,因为之前标准输出已经重定向到了空设备文件,所以标准错误输出也重定向到空设备文件。<h1 id="gitlab">gitlab</h1>
<h2 id="安装完成后注意的问题">安装完成后注意的问题</h2>
deb安装包安装完成后,找不到相应的文件说明,配置时反而更加麻烦。而且安装后使用chef来配置环境,不熟悉chef情况下,完全搞不明白错误的原因,因此,最好的办法还是以源码的方式逐步安装。
<https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md>由于涉及多种服务,而且这些目录需要在同一目录之下,因此,最好的目录安排方式就是官方推荐的方式:
luyanfei@infojhc:/home/git$ sudo tree -L 1
.
├── gitlab
├── gitlab-git-http-server
├── gitlab-shell
├── repositories
└── tmp
gitlab默认情况下访问redis服务器是通过unix
socket来连接,并且没有密码,如果要设定密码,除了在配置文件中修改以外,需要额外修改application.rb文件,添加如下代码行:
example
redis_config_hash[:password] = 'password'
```
经过实践,发现额外添加密码来连接redis会导致某些服务无法启动,看样子,目前版本的gitlab把socket连接写死,无法配置。因此最好的办法还是遵守unix
socket的连接方式。
要注意把/etc/skel下的.profile等文件拷贝到/home/git目录下,采用自编译安装ruby时,bundle等工具的路径为/usr/local/bin,如果不拷贝这些文件,cron任务执行时会提示找不到bundle。