Gitlab使用非绑定的Nginx

1、编辑gitlab.rb

vi /etc/gitlab/gitlab.rb

配置如下:

nginx['enable'] = false
#nginx的用户
web_server['external_users'] = ['nginx']
#nginx服务器的地址
gitlab_rails['trusted_proxies'] = [ '192.168.1.0/24', '192.168.2.1', '2001:0db8::/32' ]

2、配置Nginx

vi /etc/nginx/conf.d/gitlab-omnibus-nginx.conf

添加以下内容:

## GitLab 8.3+
##
## Lines starting with two hashes (##) are comments with information.
## Lines starting with one hash (#) are configuration parameters that can be uncommented.
##
##################################
## CONTRIBUTING ##
##################################
##
## If you change this file in a Merge Request, please also create
## a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
##
###################################
## configuration ##
###################################
##
## See installation.md#using-https for additional HTTPS configuration details.

upstream gitlab-workhorse {
server unix:/var/opt/gitlab/gitlab-workhorse/socket;
}

## Normal HTTP host
server {
## Either remove "default_server" from the listen line below,
## or delete the /etc/nginx/sites-enabled/default file. This will cause gitlab
## to be served if you visit any address that your server responds to, eg.
## the ip address of the server (http://x.x.x.x/)n 0.0.0.0:80 default_server;
listen 0.0.0.0:80 default_server;
listen [::]:80 default_server;
server_name git.dream7788.com; ## Replace this with something like gitlab.example.com
server_tokens off; ## Don't show the nginx version number, a security best practice
root /opt/gitlab/embedded/service/gitlab-rails/public;

## See app/controllers/application_controller.rb for headers set

## Individual nginx logs for this GitLab vhost
access_log /var/log/nginx/gitlab_access.log;
error_log /var/log/nginx/gitlab_error.log;

location / {
client_max_body_size 0;
gzip off;

## https://github.com/gitlabhq/gitlabhq/issues/694
## Some requests take more than 30 seconds.
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_redirect off;

proxy_http_version 1.1;

proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

proxy_pass http://gitlab-workhorse;
}
}

添加nginx用户到git用户组

usermod -a -G git nginx

3、重新配置gitlab

gitlab-ctl reconfigure
gitlab-ctl start

WordPress定期出现“建立数据库连接时出错”问题的解决方案

先贴阿里云服务器的配置:

未分类

这是一个困扰我很久的问题,之前不管是搭在CentOS还是Ubuntu上,Wordpress都会定期出现“建立数据库连接时错误”,最近一周更是每天早上网站都无法访问。通过 netstat -tunlp 发现是MySQL的进程被干掉了,service mysqld restart 还重启不了,简单粗暴点就直接reboot。不过每天早上起床先reboot也不是个办法,而且我猜测网站频繁连不上会被搜索引擎降权,所以问题还是要解决的。百度“wordpress建立数据库连接时出错”,搜索结果可以归为以下几类:

  • 安装WordPress时出现这个问题,而我是运行时定期出现,没用。
    修改localhost为127.0.0.1,没用。

  • define(‘WP_ALLOW_REPAIR’, true); 让WordPress自行修复数据库,没用。

  • 修改 php.ini 文件里 mysql.default_socket、mysqli.default_socket、pdo_mysql.default_socket 的值为/tmp/mysql.sock,没用。

就在我快要放弃挣扎的时候,看到了 https://bbs.aliyun.com/read/269743.html 这篇文章,跟着作者的指引,问题竟然给解决了。总结一下:

起因

简单来说就是PHP-FPM子进程过多,吃光了内存,MySQL的进程就被干掉了。(实际上这个里面涉及到的知识点非常多,我目前还不具备完全搞清楚的知识储备,参考的那篇文章里也没有讲彻底。)

论证

百度“在线压力测试”,看到阿里云的广告,点之,免费试用性能测试铂金版。

未分类

未分类

未分类

其实并发调到6,7的时候就扛不住了。free -m 内存所剩无几;netstat -tunlp MySQL进程被kill;访问网站,出现“建立数据库连接时出错”,问题得到复现。

解决方案

编辑 php-fpm.conf 文件,控制PHP-FPM的子进程数。

操作步骤

1、找到php安装目录etc目录下的 php-fpm.conf 文件。

2、找到 pm 、pm.start_servers 、pm.min_spare_servers 、pm.max_spare_servers 这几项参数,修改参数的值。

pm = dynamic                        #php-fpm以动态模式运行,动态模式适合内存较小的服务器
pm.start_servers = 3                #动态模式下,php-fpm的起始进程数
pm.min_spare_servers = 3            #动态模式下,php-fpm的最小进程数
pm.max_spare_servers = 10           #动态模式下,php-fpm的最大进程数

3、service php-fpm restart 重启php-fpm

此时开启性能测试,发现MySQL的进程不会再被干掉,网站也不再出现“建立数据库连接时出错”的问题。收工。

三种方法解决升级更新 WordPress 速度慢的问题

如果你的 WordPress 搭建在国内服务器上,那你肯定遇到过 WordPress 更新失败的问题。我在阿里云上实测下载官方安装包速度不过每秒十几 k。那么除了官方繁琐的“三步”升级法(手动覆盖文件)外还有其他更高效的更新方法吗?

方法一:WP-CLI

WP-CLI 是官方推出的一款 WordPress 命令行工具。无需浏览器,你就可以实现快速安装、更新主题、配置站点、搜索替换等几乎所有可以想到的功能。

如何安装 WP-CLI 这里不做说明,请移步官方安装指南: https://make.wordpress.org/cli/handbook/installing/

假如你已经安装好了 WP-CLI,那么首先通过 wp core check-update 命令获取新版的安装包地址,然后通过自己的途径下载并放到服务器上。

$ wp core check-update
+---------+-------------+-----------------------------------------------------------------+
| version | update_type | package_url                                                     |
+---------+-------------+-----------------------------------------------------------------+
| 4.9     | major       | https://downloads.wordpress.org/release/zh_CN/wordpress-4.9.zip |
+---------+-------------+-----------------------------------------------------------------+

最后执行 wp core update path/to/zip/file 命令升级 WordPress,输出的内容和你通过网页升级类似。

$ wp core update path/to/zip/file
Starting update...
正在解压缩升级文件...
...
Cleaning up files...
No files found that need cleaned up.
Success: WordPress updated successfully.

安装过程中,可能会自动下载语言包,不过语言包很小,没什么影响。这样你就成功升级了 WordPress~

方法二:修改 WordPress 升级代码

或者我们可以修改 WordPress 升级代码逻辑,如果下载的文件是更新包,就直接指向本地文件,而不要远程下载。

编辑 /wp-admin/includes/class-wp-upgrader.php 文件,搜索 $download_file = download_url($package);,作以下修改:

public function download_package( $package ) {
    ...

    // 如果想知道原来的下载地址,可以取消注释下面这行,然后在网页后台点更新获得。
    // exit($package);

    if ($package == 'https://downloads.wordpress.org/release/zh_CN/wordpress-4.9.zip') {
        $download_file = 'path/to/zip/file';
    } else {
        $download_file = download_url($package);
    }

    ...
}

方法三:使用代理服务器

打开 wp-config.php 文件,在底部增加以下代码:

define('WP_PROXY_HOST', 'us.webres.wang');
define('WP_PROXY_PORT', '31281');

* 该示例代理服务器来源于网络。

Linux CentOS环境下单机安装tomcat多实例

平台: centos 6.9
安装版本:tomcat 9.0 Binary Distributions

安装目的:安装三个tomcat,对外 web端口分别为8080、8081、8082
原理: 多个tomcat实例公用一个JDK,通过安装目录、端口以及配置文件实现独立实例,每个tomcat猫 都有自己独立的运行环境、配置文件 和站点目录。

按照系统的部署流程记录如下:

1、安装JDK

yum search java,这里我们选取java-1.8.0-openjdk.x86_64 : OpenJDK Runtime Environment。

yum install java-1.8.0-openjdk.x86_64   -y

2、下载安装tomcat

下载地址https://tomcat.apache.org/,这里我们选取tomcat版本后对应下载,以Binary Distributions 9.0做测试。

wget  http://mirrors.shuosc.org/apache/tomcat/tomcat-9/v9.0.1/bin/apache-tomcat-9.0.1.tar.gz   -P  /usr/local/src
cd  /usr/local/src
tar zxvf apache-tomcat-9.0.1.tar.gz 
mv apache-tomcat-9.0.1 tomcat_8080
#说明:这里不建议直接mv到/usr/local目录中,因为这个是原始的tomcat,以后创建多实例可以直接从这里拷贝并优化。
#配置单实例tomcat8080
mkdir  /usr/local/tomcat_8080
cp -rfpu  /usr/local/src/tomcat_8080/* /usr/local/tomcat_8080/

启动tomcat8080:

/usr/local/tomcat_8080/bin/startup.sh

查看效果:

[root@mysqlemn local]# netstat  -tunlp | grep 8080
tcp        0      0 :::8080                     :::*                        LISTEN      13240/java

如果防火墙开启,需要加一个规则进去以便web访问:

iptables  -I  INPUT -p tcp  --dport  8080  -j ACCEPT

web访问测试:

未分类

3、多实例安装

刚才创建了一个8080,下边我们再创建两个tomcat,端口分别为8081、8082。 如果熟练了,可以直接跳过8080,直接创建需要的实例个数。
创建多个tomcat目录:

mkdir -p /usr/local/tomcat_808{1..2}
cp  -rfpu  /usr/locat/src/tomcat_8080/*  /usr/local/tomcat_8081/
cp  -rfpu  /usr/locat/src/tomcat_8080/*  /usr/local/tomcat_8082/

分别修改tomcat的 conf目录下的server.xml,修改三个端口设置(Server、Connector、AJP,至于https根据 是否用到修改),防止和之前的tomcat_8080冲突。修改以后分别启动测试:

/usr/local/tomcat_8081/bin/startup.sh
/usr/local/tomcat_8082/bin/startup.sh

iptables 添加端口并save,web单独访问,单机多实例猫已经实现。剩下的就是根据业务需要、服务器配置进行tomcat的配置优化了,比如包括服务设置、站点发布目录以及 jvm优化等等。

说明:

(1),站点发布目录修改:

未分类

重启tomcat生效。

(2),这里建议将站点目录的catalina.sh设置成tomcatX放于/etc/init.d/tomcatPort ,里边设置下java路径以及环境启动目录比如:

[root@21yunwei ROOT]# cp   /usr/local/tomcat_8080/bin/catalina.sh      /etc/init.d/tomcat8080  -p
[root@21yunwei ROOT]# cp   /usr/local/tomcat_8081/bin/catalina.sh      /etc/init.d/tomcat8081  -p
[root@21yunwei ROOT]# cp   /usr/local/tomcat_8082/bin/catalina.sh      /etc/init.d/tomcat8082  -p

针对每个服务设置自己的启动目录:这里以/etc/init.d/tomcat8080 为例进行修改,第二行加入:

CATALINA_HOME=/usr/local/tomcat_8080 #tomcat容器的启动目录

其他容器的服务也是一样,这样以后就可以通过/etc/init.d/tomcatPort start 启动或者stop了,同时这个可以设置到/etc/rc.local脚本中方便开机启动,目前我们线上就是这样实现。

/etc/init.d/tomcat8080  start
/etc/init.d/tomcat8081  start
/etc/init.d/tomcat8082  start

(3),清理tomcat缓存。解决访问有异常,清理下tomcat服务器端的缓存方法:

find /usr/local/ -name  "work*" | xargs   rm -rf

说明:这里只是针对tomcat安装测试,未进行任何优化。

CentOS 服务器修改或增加 SSH 端口

默认 SSH 默认端口为 22,所有服务器都一样,所以比较容易被别人扫描,尝试暴力破解登录。比较简单的办法,就是修改默认端口了。

修改 SSH 端口的方法本来挺简单的,网上也是一搜一把。

下面以修改为 10086 端口为例

1. 修改 /etc/ssh/sshd_config 文件,找到 Port 22 ,在其下方,增加 Port 10086

vi /etc/ssh/sshd_config

注意,这里是增加,而不是修改,主要是为了防止改了之后,新端口不能正确登录的麻烦。所以先新增,然后测试新端口能正确登录了,再将 Port 22 行注释掉即可。

2. 重启 sshd 服务

systemctl restart sshd

一般到这里也就可以试试新端口是否正确工作了。

但是阿里云服务器 ECS 有个安全组策略,需要手动设置其开放 10086 端口的访问:

3. 进入云服务器实例管理界面,左侧菜单“本实例安全组” > 配置规则,找到自定义 TCP (22/22)规则条目,> 克隆,修改协议类型为 “自定义 TCP”,端口范围设置为 10086/10086 ,提交保存即可。

如果经过上面步骤,新端口 SSH 依然连接超时,那么可能需要检查服务器防火墙设置,以及 SELinux 之类配置。

一般阿里云服务器,因为有上述安全组策略的存在,应该是默认没有开启防火墙的。因此前不知道阿里的安全组策略,我把下面几步都做了:

关于 SELinux 和 防火墙的相关配置和描述,请参考 皓煙:http://blog.csdn.net/ausboyue/article/details/53691953
对于使用 iptables 的服务器,请参考 http://blog.csdn.net/default7/article/details/42015409

在新端口连接测试通过之后,注意参考上述第 1 步注释掉原来的 22 端口配置,当然,也可以参考上述第 3 步,删除阿里 ECS 安全组策略里边的 自定义 ICP (22/22)规则条目。

因为 ECS 的安全组策略我是在走完 SELinux 和 防火墙,甚至包括 iptabls 等流程之后,依然连接超时,之后才知道的。现在我也在想:既然有了凌驾于服务器防火墙配置之上的 安全组策略存在了,那么防火墙、SELinux 之类的是不是也可以禁用了呢?

Linux下配置SSH无密码登录

  • 用ssh-keygen创建公钥
[root@qzweb1 .ssh]# ssh-keygen -t rsa    //生成密钥
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):    //回车,使用默认目录
Enter passphrase (empty for no passphrase):     //直接回车设置空密码           
Enter same passphrase again:            //也是回车
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
e0:be:39:e4:08:a0:4c:e7:16:f5:bc:80:68:14:59:8a root@qzweb1
The key's randomart image is:
+--[ RSA 2048]----+
| .+.             |
|.o.  .           |
|E.. o +          |
|.+ + o +         |
|=.o . o S        |
|...o ...         |
|  .. +.          |
|    . oo         |
|      o.         |
+-----------------+
  • 查看生成出来的密钥
[root@qzweb1 .ssh]# cd ~/.ssh/
[root@qzweb1 .ssh]# ls -l
总用量 12
-rw-------  1 root root 1675 11月 23 15:14 id_rsa
-rw-r--r--  1 root root  393 11月 23 15:14 id_rsa.pub
-rw-r--r--. 1 root root 3176 11月 22 10:45 known_hosts
[root@qzweb1 .ssh]#
  • 下发公钥至目标机器
[root@qzweb1 .ssh]# scp id_rsa.pub 192.168.113.132:~/.ssh/
[email protected]'s password: 
id_rsa.pub                                                                                                                                                                                                                                  100%  393     0.4KB/s   00:00    
[root@qzweb1 .ssh]#
  • 配置目标机并设置文件和目录权限
[root@qzweb2 ~]# cd ~/.ssh/
[root@qzweb2 .ssh]# ls
id_rsa.pub
[root@qzweb2 .ssh]# cat id_rsa.pub >> authorized_keys
[root@qzweb2 .ssh]# chmod 600 authorized_keys  
[root@qzweb2 .ssh]# chmod 700 -R ~/.ssh 
[root@qzweb2 .ssh]#
  • 使用SSH进行连接
[root@qzweb1 .ssh]# ssh 192.168.113.132
Last login: Thu Nov 23 13:42:21 2017 from 192.168.81.24

相关说明

创建的~/.ssh/id_rsa、id_rsa.pub的文件,其中第一个为密钥,第二个为公钥。过程中会要求输入密码,为了ssh访问过程无须密码,可以直接回车。

ssh-keygen:生成秘钥的部分参数:
-t指定算法
-f 指定生成秘钥路径
-N 指定密码

相关说明2

ssh-copy-id 命令可以把本地的ssh公钥文件安装到远程主机对应的账户下。用法:

ssh-copy-id -i /root/.ssh/id_rsa 192.168.1.11

0
The authenticity of host '192.168.1.11 (192.168.1.11)' can't be established.
RSA key fingerprint is 6e:34:d4:8c:fb:72:72:3a:49:7a:14:23:20:59:ea:28.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.1.11' (RSA) to the list of known hosts.
[email protected]'s password: (输入192.168.1.11 root密码)
Now try logging into the machine, with "ssh '192.168.2.11'", and check in:

  .ssh/authorized_keys

to make sure we haven't added extra keys that you weren't expecting.

ssh-copy-id -i /root/.ssh/id_rsa 192.168.1.12
同上

ssh-copy-id -i /root/.ssh/id_rsa 192.168.1.13
同上

sftp上传文件到linux服务器上(ssh验证)

需求:

以前,手动上传配置文件到服务器,然后手工复制到另外一台服务器上,然后登陆SSH Secure File Transfer Client客户端,执行相关shell命令号
现在这些操作需要一键完成,即文件复制到另一台服务器,登陆ssh客户端,切换用户,执行导入命令行
解决办法:

  • 获得应用程序所在的机器用户名和密码,然后执行shell脚本完成以上操作
  • 未采用:因为运维不提供应用服务器的用户名和密码
    直接连接另一台服务器,执行复制文件,然后执行shell脚本(采取)
    要上传文件到linux服务器上,使用FTP协议,公司linux服务器上需要ftp客户端需要安全验证,比较麻烦,所以想到使用sftp来解决,sftp是基于ssh的安全协议的ftp协议,Java中有JSch包来实现连接服务器。

第一步:下载JSch包,请从官网下载它:http://www.jcraft.com/jsch/
第二步:新建FtpsFileList.java文件

//只有上传方法,也可以有下载文件方法
public class FtpsFileList {
    private static final Logger LOG = LoggerFactory.getLogger(FtpsFileList.class);

    //将本地的dirFile这个文件复制到远程服务器上的desFile文件夹下
    public static void loadFile(String host, int port, String username, final String password, String dirFile,String desFile) {
        ChannelSftp sftp = null;
        Channel channel = null;
        Session sshSession = null;
        try {
            JSch jsch = new JSch();//创建一个jsch对象
            jsch.getSession(username, host, port);
            // 根据用户名,主机ip,端口获取一个Session对象
            sshSession = jsch.getSession(username, host, port);
            //设置密码
            sshSession.setPassword(password);
            Properties sshConfig = new Properties();
            sshConfig.put("StrictHostKeyChecking", "no");
            // 为Session对象设置properties
            sshSession.setConfig(sshConfig);
            sshSession.connect();
            LOG.debug("Session connected!");
            // 打开SFTP通道
            channel = sshSession.openChannel("sftp");
            // 建立SFTP通道的连接
            channel.connect();
            LOG.debug("Channel connected!");
            sftp = (ChannelSftp) channel;

            //InputStream is = sftp.get("/ftp/re/20140713.dat");
            InputStream fis = new FileInputStream(new File(dirFile));
            //文件上传(通过inputstream流来实现)  详见https://www.cnblogs.com/longyg/archive/2012/06/25/2556576.html
            sftp.put(fis,desFile);
            LOG.info("文件复制到服务器成功!r");

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            closeChannel(sftp);
            closeChannel(channel);
            closeSession(sshSession);
        }
    }

    private static void closeChannel(Channel channel) {
        if (channel != null) {
            if (channel.isConnected()) {
                channel.disconnect();
            }
        }
    }

    private static void closeSession(Session session) {
        if (session != null) {
            if (session.isConnected()) {
                session.disconnect();
            }
        }
    }
}

调用方式:

FtpsFileList.loadFile("10.31.92.70", 22, "serviceop", "115LbUrAbEsZw","/nfsc/qhcs-ansir-stg1/ansir/HanLP/xiaodai/loan-freebase.ttl","/wls/serviceop/virtuoso_script/loanXD.ttl");
logger.info("ttl文件复制成功");

到这一步,已经完成上传文件到linux服务器上

第三步:写好shell脚本(关键核心),这一步学习了好多东西,发现shell命令挺好玩的

#!/usr/bin/expect
spawn ssh [email protected]
set timeout 2
expect "password:"
send "115LbUrAbEsZwr"
expect "*]#"
set password "wuxin952"
spawn su root
expect "password:"  
send "wuxin952r" 
expect "#"
send "/wls/serviceop/virtuoso-opensource/home/bin/isql localhost:13002r" 
send "DB.DBA.TTLP_MT(file_to_string_output('/wls/serviceop/virtuoso_script/loan.ttl'),'','http://www.xiaowei.com');r"
interact

注释:因为需要交互式输入密码,所以选择使用expect命令环境来执行
具体释义见:

第四步:java代码调用shell脚本,代码如下,
Process类是一个抽象类,用于定义一个本地进程,Runtime.getRuntime().exec(sh)返回一个进程对象
具体见:process

//授予权利给shell脚本呢
Process ps1=Runtime.getRuntime().exec("chmod 777 /nfsc/qhcs-ansir-stg1/ansir/HanLP/xiaodai/bash_scp.sh");
//等待当前线程执行完,等待返回process类对象表示的进程结束
ps1.waitFor();
logger.info("chmod命令执行结束");

BufferedReader br1 = new BufferedReader(new InputStreamReader(ps1.getInputStream()));
while ((line = br1.readLine()) != null) {
    logger.info("chmod命令结果:"+line);
}
//实际调用使用expect 文件
Process ps2=Runtime.getRuntime().exec("expect /nfsc/qhcs-ansir-stg1/ansir/HanLP/xiaodai/bash_scp.sh");
ps2.waitFor();
logger.info("expect命令执行结束");

BufferedReader br2 = new BufferedReader(new InputStreamReader(ps2.getInputStream()));
while ((line = br2.readLine()) != null) {
    logger.info("expect命令结果:"+line);
}

String result = sb.toString();
logger.info("expect整理结果为:"+result);

记一次诡异的 ssh 互信免密码登录失败

0、背景

因为 hadoop 环境需要 master 能免密码 ssh localhost,所以我们需要建立与本机 localhost 的互信,方法很简单:

1. ssh-keygen -t rsa
   #Press enter for each line 
2. cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
3. chmod og-wx ~/.ssh/authorized_keys 

这三步执行下来就能顺利 ssh localhost 免密码登录了,但是昨天刚建好的互信,今天下午突然不能用了,ssh localhost 需要密码,第一反应是可能哪里设置和配置被改动了,看了下文件版本、配置修改时间都无变化,然而登录时的提示信息又过于简单,这个时候排查陷入僵局了。

work@test_zz_Master 192.168.187.213 18:45:18 ~ >
ssh localhost            
work@localhost's password: 

work@test_zz_Master 192.168.187.213 18:45:24 ~ >

1、怎么排查?

1.1 debug 日志

首先还是要拿到明细 debug 日志,看看卡在哪里了。linux 下的不少命令都自带调试功能,比如 ssh 就自带 debug 功能:

ssh -vvv localhost
OpenSSH_5.3p1, OpenSSL 1.0.1e-fips 11 Feb 2013
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: Applying options for *
debug2: ssh_connect: needpriv 0
debug1: Connecting to localhost [127.0.0.1] port 22.
debug1: Connection established.
debug1: identity file /home/work/.ssh/identity type -1
debug1: identity file /home/work/.ssh/identity-cert type -1
...
debug3: remaining preferred: keyboard-interactive,password
// 启用公钥登录
debug3: authmethod_is_enabled publickey
debug1: Next authentication method: publickey
debug1: Trying private key: /home/work/.ssh/identity
debug3: no such identity: /home/work/.ssh/identity
debug1: Offering public key: /home/work/.ssh/id_rsa
debug3: send_pubkey_test
// 发送公钥包,等待服务器认证响应
debug2: we sent a publickey packet, wait for reply
debug3: Wrote 368 bytes for a total of 1741
debug1: Authentications that can continue: publickey,gssapi-keyex,gssapi-with-mic,password
debug1: Trying private key: /home/work/.ssh/id_dsa
debug3: no such identity: /home/work/.ssh/id_dsa
debug1: Trying private key: /home/work/.ssh/id_ecdsa
debug3: no such identity: /home/work/.ssh/id_ecdsa
// 没通过认证,禁用该认证方法
debug2: we did not send a packet, disable method
debug3: authmethod_lookup password
debug3: remaining preferred: ,password
debug3: authmethod_is_enabled password
// 下一个认证方法:启用密码登录
debug1: Next authentication method: password
work@localhost's password: 

可以看到,确实是认证失败了,但是仅凭一句 we did not send a packet, disable method,咱们还是无法看到失败的深层次原因,那咱们再对比下正常的认证流程应该是怎样的:

未分类

可以看到右边正常的会接受公钥,左边的则没有得到响应,继续走别的认证方式。

1.2 检查配置

打开服务器的 /etc/ssh/sshd_config

确认下面几行是这样的:

RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile      .ssh/authorized_keys

#GSSAPIAuthentication yes
#GSSAPICleanupCredentials yes

配置没问题,此路还是不通。

1.3 Debugging SSH public key

在B机器上,we sent a public key packet, wait for reply 之后则是紧跟着”debug1: Server accepts key: pkalg ssh-rsa blen 277″。由此可以看出,是A机器的sshd不认可publickey。

至于为什么不认可,在google上查了许多,毫无头绪,直到使用类似“ssh publickey ignore debug diagnose”这样的关键词,发现这个页面,其中的第二条和第六条给出了解答:

2. Debugging on the remote host by running sshd in debug mode: Run ‘/usr/sbin/sshd -d -p 2222′ on the remote host and connect to it. ’2222′ here is the port number of the sshd process you started on the remote host.

6. Check the permissions on your home directory, .ssh directory, and the authorized_keys file: If your ssh server is running with ‘StrictModes on’, it will refuse to use your public keys in the ~/.ssh/authorized_keys file. Your home directory should be writable only by you, ~/.ssh should be 700, and authorized_keys should be 600.

通过执行 /usr/sbin/sshd -d -p 2222 (在2222端口启动一个带debug输出的sshd) ,

然后 ssh -vv localhost -p 2222 ,可以看到 sshd 的输出:

[root(hostname)@bjdhj-187-213 ~]# /usr/sbin/sshd -d -p 2222
debug1: sshd version OpenSSH_5.3p1
debug1: read PEM private key done: type RSA
...
debug1: trying public key file /home/work/.ssh/authorized_keys
debug1: fd 4 clearing O_NONBLOCK
Authentication refused: bad ownership or modes for directory /home/work
debug1: restore_uid: 0/0
debug1: temporarily_use_uid: 500/500 (e=0/0)
debug1: trying public key file /home/work/.ssh/authorized_keys
debug1: fd 4 clearing O_NONBLOCK
Authentication refused: bad ownership or modes for directory /home/work
debug1: restore_uid: 0/0
Failed publickey for work from 127.0.0.1 port 45548 ssh2

可以看到倒数第三行:Authentication refused: bad ownership or modes for directory /home/work,

正好与那第六条相对应,再检查一下 /home/work ,其权限是否是其他组可读写。

同时,咱们也能从 /var/log/secure 看到明细的 debug 日志:

[root(hostname)@bjdhj-187-213 ~]# tail -f /var/log/secure
Sep  1 18:52:20 bjdhj-187-213 sshd[30936]: Server listening on 0.0.0.0 port 22.
Sep  1 18:52:23 bjdhj-187-213 sshd[30944]: Authentication refused: bad ownership or modes for directory /home/work
Sep  1 18:52:23 bjdhj-187-213 sshd[30944]: Authentication refused: bad ownership or modes for directory /home/work
Sep  1 18:52:25 bjdhj-187-213 sshd[30948]: Connection closed by 127.0.0.1

2、最终解决方案

ssh 为了保证通信安全,防止 key 被篡改或窃取,对目录和文件的权限要求相当严格,

咱们最终需要确保相关目录权限与下述一致:

chmod 0755 ~               # 或 chmod g-w ~   
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

sudo service sshd restart

后记:

当然了,这篇文章所反映的问题虽然很小,最后的答案也很简单,但是其展现的排查思路和方法却很独特,值得借鉴,毕竟很多时候咱们不能像平时一样,直接 debug 源码。

使用 autossh 建立反向 SSH 隧道管理个人计算机

设你有这样一个需求:你在家中有一台 Linux/Unix 设备,可以是路由器、NAS 或者台式机,存有自己常用工具或者数据,想要在自己外出时也能随时访问。那么你现在的目的和我一样了,你所需要的是少许 Linux/Unix 经验以及一台能够从公网访问的中继服务器。我们所需要的技术是通过 SSH 隧道搭建一个反向代理。

配置

在你的 SSH 配置文件中加入这一行 GatewayPorts clientspecified。可以直接使用命令:sudo echo “GatewayPorts clientspecifie” >> /etc/ssh/sshd_config。

然后重新加载 SSH 配置文件:sudo reload ssh。

然后在本地建立连接: ssh -f -R 0.0.0.0:20000:localhost:22 local_user@a_a_a_a

现在还有两个问题:你需要保持终端开启防止 SSH 进程被关闭;由于网络故障/波动导致 SSH 终断时无法自动重连。

前者可以使用 -N 参数来解决,后者需要 supervisor 等第三方监控工具。

使用 autossh 代替 ssh

不过我们还有一个更常用的选择方案:autossh:

autossh -M 20001 
-fN -o "PubkeyAuthentication=yes" 
-o "StrictHostKeyChecking=false" -o "ServerAliveInterval 60" -o "ServerAliveCountMax 3" 
-R a_a_a_a:20000:localhost:22 
-p 8383 remote_user@a_a_a_a

说明:

  • -M 20001 选项指定中继服务器上的监视端口,用于交换监视 SSH 会话的测试数据,需要保证该端口在服务器上未被占用。
  • -o 用于设置 autossh 参数。
  • -f 指定 autossh 在后台运行,并不会传给 ssh。和 ssh 的 -f 不一样,autossh 指定 -f 时将无法寻求密码。指定 -f 时,会将环境变量 AUTOSSH_GATETIME 覆盖为 0!

开机启动

在 Ubuntu 中我们可以使用 systemd 管理 autossh 的开机启动问题(旧版本中可以使用 init.d)。配置很简单,只需要创建一个 /etc/systemd/system/remote-autossh.service 文件:

[Unit]
Description=AutoSSH service for remote tunnel
After=network-online.target

[Service]
User=your_username
ExecStart=/usr/bin/autossh -M 20001 -N -o "PubkeyAuthentication=yes" -o "StrictHostKeyChecking=false" -o "ServerAliveInterval 60" -o "ServerAliveCountMax 3" -R a_a_a_a:20000:localhost:22 -p 8383 remote_user@a_a_a_a

[Install]
WantedBy=multi-user.target

这样就创建了一个 remote-autossh 服务,并指定其在网络服务启动后启动。可以运行 systemctl daemon-reload && systemctl start remote-autossh 立即启动服务,或者 systemctl enable remote-autossh.service 启动服务并设置为开机启动。

需要注意的是,配置文件中的 autossh 命令需要替换为其绝对地址,以及不支持 -f 参数。

Saltstack模块file发送中文名称文件问题解决

最近又用到了saltstack,发现过了这么多年,salt的file模块无法发送中文名称文件问题还没有人解决。
蛋蛋的忧伤啊,国内这么流行的东西既然不支持中文。
于是从昨晚一直决战到今天天亮,终于找到了可行性方法。
下面做个笔记,希望能帮到有需要的人。

一、系统环境

系统:CentOS7.2
python版本:2.7.5
salt版本:2015.5.10

二、问题展现

需求:要同步一个文件夹(同步文件或文件夹一样)到minion端,文件夹里包含中文名称的文件
执行过程报错:

[plain] view plain copy
192.168.1.127:  
----------  
          ID: dir_send  
    Function: file.recurse  
        Name: /tmp/ylhb  
      Result: False  
     Comment: An exception occurred in this state: Traceback (most recent call last):  
                File "/usr/lib/python2.7/site-packages/salt/state.py", line 1564, in call  
                  **cdata['kwargs'])  
                File "/usr/lib/python2.7/site-packages/salt/states/file.py", line 2401, in recurse  
                  ) for (k, v) in six.iteritems(ret['comment'])).strip()  
                File "/usr/lib/python2.7/site-packages/salt/states/file.py", line 2401, in <genexpr>  
                  ) for (k, v) in six.iteritems(ret['comment'])).strip()  
              UnicodeDecodeError: 'ascii' codec can't decode byte 0xe9 in position 10: ordinal not in range(128)  
     Started: 16:16:17.297668  
    Duration: 23.924 ms  
     Changes:     

Summary  
------------  
Succeeded: 0  
Failed:    1  
------------  
Total states run:     1  
ERROR: Minions returned with non-zero exit code  

查看python默认编码:

[plain] view plain copy
>>> import sys  
>>> sys.getdefaultencoding()  
'ascii'  
>>>   

可知默认的编码是ascii。
由于python默认编码为ascii,当程序中出现非ascii编码时,python的处理常常会报“UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xe9 in position 10: ordinal not in range(128)”这样的错(python3不会有这样的问题)。

三、解决方法

只需修改python默认编码为“utf-8”即可(master端和minion端都要修改),修改方式如下:
新增文件/usr/lib/python2.7/site-packages/sitecustomize.py,内容如下:

[python] view plain copy
# -*- coding: UTF-8 -*-  
import sys   
reload(sys)  
sys.setdefaultencoding('utf-8')  

再次查看python默认编码:

[python] view plain copy
>>> import sys  
>>> sys.getdefaultencoding()  
'utf-8'  
>>>   

重启salt服务(master端执行“systemctl restart salt-master”,minion端执行“systemctl restart -salt-minion”)。
再次执行发送中文名称文件:

[plain] view plain copy
192.168.1.127:  
----------  
          ID: dir_send  
    Function: file.recurse  
        Name: /tmp/ylhb  
      Result: True  
     Comment: Recursively updated /tmp/ylhb  
     Started: 17:06:40.381259  
    Duration: 71.074 ms  
     Changes:     
              ----------  
              /tmp/ylhb/雨落寒冰:  
                  ----------  
                  diff:  
                      New file  
                  mode:  
                      0644  

Summary  
------------  
Succeeded: 1 (changed=1)  
Failed:    0  
------------  
Total states run:     1  

以上发送中文名称文件成功。

注:以上仅适用于Linux系列系统,暂不支持Windows(闭源太麻烦了,已经很多年没用了,试着把python升级到3以上版本解决吧)