2012年5月7日星期一

OpenWRT AutoSSH

我没有VPN,并且OpenWRT默认的ssh客户端dropbear不能用来创建socks代理,并且我从SSHChina买来的帐户不支持证书登陆,所以得改个OpenSSH并且重新编译。

OpenWRT的SDK编译起来并不麻烦,只是有点耗时间。为OpenSSH写补丁也不难,将下面的内容存为999-env-pwd.patch,放到feeds/packages/net/openssh/patches下编译即可。

--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -866,6 +866,7 @@
  static int attempt = 0;
  char prompt[150];
  char *password;
+ char *env_pwd = getenv("OPENSSH_PASSWORD");
  const char *host = options.host_key_alias ?  options.host_key_alias :
      authctxt->host;
 
@@ -875,17 +876,23 @@
  if (attempt != 1)
   error("Permission denied, please try again.");
 
- snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ",
-     authctxt->server_user, host);
- password = read_passphrase(prompt, 0);
+ if (env_pwd == NULL) {
+  snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ",
+   authctxt->server_user, host);
+  password = read_passphrase(prompt, 0);
+ }
  packet_start(SSH2_MSG_USERAUTH_REQUEST);
  packet_put_cstring(authctxt->server_user);
  packet_put_cstring(authctxt->service);
  packet_put_cstring(authctxt->method->name);
  packet_put_char(0);
- packet_put_cstring(password);
- memset(password, 0, strlen(password));
- xfree(password);
+ if (env_pwd == NULL) {
+  packet_put_cstring(password);
+  memset(password, 0, strlen(password));
+  xfree(password);
+ } else {
+  packet_put_cstring(env_pwd);
+ }
  packet_add_padding(64);
  packet_send();

这种东西因为账户安全的原因,最好自己编译。不过我还是放了一个上来,放心的话,就用这个吧:https://www.boxcn.net/s/33ceb35647f5d510f1b2

(因为ps能看到所有进程的命令行,所以我改的方式是从OPENSSH_PASSWORD环境变量中读取登陆密码,用起来是这个样子:OPENSSH_PASSWORD=pwd ssh -CfNg -D 192.168.1.1:7070 user@host)

将下载或编译好的ssh放到/opt/bin/ssh下,然后创建一个ssh的配置文件/etc/ssh/ssh_config:

Host *.sshchina.com
  StrictHostKeyChecking no

Host可以指定IP地址,但似乎不能一行写好几个。StrictHostKeyChecking no的意思是,自动接受指定服务器的证书而不询问用户。

接下来,opkg install autossh安装好autossh。打开/etc/config/autossh,改成类似下面这样:

config autossh
        option ssh      '-CfNg -D 0.0.0.0:7070 root@host'
        option password 'pwd'
        option gatetime '0'
        option monitorport      '20000'
        option poll     '600'

再打开/etc/init.d/autossh,将start_instance段改成:

start_instance() {
        local section="$1"

        config_get ssh "$section" 'ssh'
        config_get gatetime "$section" 'gatetime'
        config_get monitorport "$section" 'monitorport'
        config_get poll "$section" 'poll'
        config_get password "$section" 'password'

        export AUTOSSH_PATH="/opt/bin/ssh"
        export OPENSSH_PASSWORD="$password"
        AUTOSSH_GATETIME="${gatetime:-30}" \
        AUTOSSH_POLL="${poll:-600}" \
        service_start /usr/sbin/autossh -M ${monitorport:-20000} -f ${ssh}
}

然后启动并启用autossh:

/etc/init.d/autossh start
/etc/init.d/autossh enable

HTTP代理方面,Polipo没法做代理调度,所以还是opkg install privoxy安装Privoxy,这样一旦搞好,家里的所有机器都只需要改代理服务器地址就行了。唯一的麻烦是,Privoxy的配置是纯文本的,改起来不是那么容易,不过这个问题以后再想办法解决吧。

/etc/privoxy/config中需要留意的就是下面三行,默认permit-access不包括localhost,连本地访问都不放过…

listen-address  0.0.0.0:8118
permit-access  192.168.1.0/24
permit-access  127.0.0.1

Update 20130928

适用于OpenWrt 12.09正式版的ssh:https://app.boxcn.net/s/hlui5boqrkpkgijfc66h

29 条评论:

  1. 下载的文件不是ipk呀 请问如何安装?

    回复删除
  2. openssh-client和dropbear有冲突,所以我没做成ipk。把下载的文件解出来放到/opt/bin下并加上可执行属性,文中有提到的,你照做就是了。我编译的这个你未必能用,要看你的openssl是什么版本。

    回复删除
  3. 请教一下,我的openwrt没有/opt/目录怎么安装呢?

    回复删除
  4. 没有/opt/bin自己建就是了,ssh文件放进去了记得改权限。

    回复删除
  5. 自己不会编译,用你编译好的连服务器时报这个错。用不了哦...
    #openssh-client root@8.8.8.8
    OpenSSL version mismatch. Built against 1000007f, you have 1000103f

    回复删除
    回复
    1. 这句话的意思就是openssl的版本错了,你的openwrt里的比较新,我当初编译用的是比较旧的版本。

      openssl是个很基础的包,很多工具都依赖他,如果你其他的都用不上就只为了配合这个ssh还好,否则的话强制替换有可能会遇到别的问题。

      我现在用的是自己编译的12.09,等正式发布了我会放个可以用的ssh上来。可如果用的是dreambox或者别人编译的从svn仓库里拿出来的版本,那我就没办法了。

      删除
    2. 此评论已被作者删除。

      删除
  6. 此评论已被作者删除。

    回复删除
  7. 此评论已被作者删除。

    回复删除
  8. 此评论已被作者删除。

    回复删除
  9. 博主您好,我应该把ssh密码写在哪个文件里呢?
    /etc/config/autossh吗?

    谢谢!

    回复删除
    回复
    1. 我写在了/etc/init.d/autossh,OPENSSH_PASSWORD就是

      删除
    2. 此评论已被作者删除。

      删除
    3. 是把密码写在下面这句等号后边的部分是吗(去掉引号?)?
      export OPENSSH_PASSWORD="$password"

      不懂代码,不好意思呀!

      删除
    4. 不是…

      /etc/init.d/autossh你可以照抄,/etc/config/autossh才是你需要根据我的例子改的地方。

      option ssh是openssh的命令行语句,option password是密码,括号不能删。

      删除
    5. 谢谢你!终于搞好啦!

      删除
  10. 你好,能不能发一个12.09的版本啊。谢谢

    回复删除
    回复
    1. 看到你的留言才知道12.09正式发布了,不过不好意思,我手里有两个路由需要升级,折腾起来不是短时间可以搞得定的,最近没时间去搞…

      删除
    2. 那我只有耐心等待了,希望ssh能挺住,不要关闭证书登录

      删除
  11. 此评论已被博客管理员删除。

    回复删除
  12. 最近在编译的时候输出如下错误:
    Applying ./patches/100-no_cast_fix.patch using plaintext:
    patching file cipher.c

    Applying ./patches/110-no_ripemd_fix.patch using plaintext:
    patching file mac.c

    Applying ./patches/130-implicit_memset_decl_fix.patch using plaintext:
    patching file includes.h

    Applying ./patches/140-pam_uclibc_pthreads_fix.patch using plaintext:
    patching file auth-pam.c

    Applying ./patches/200-dscp-qos.patch using plaintext:
    patching file ssh_config
    patching file sshd_config

    Applying ./patches/999-env-pwd.patch using plaintext:
    patching file sshconnect2.c
    Hunk #1 succeeded at 864 (offset -2 lines).
    Hunk #2 FAILED at 876.
    1 out of 2 hunks FAILED -- saving rejects to file sshconnect2.c.rej
    Patch failed! Please fix ./patches/999-env-pwd.patch!
    make[2]: *** [/home/******/openwrt/svn/build_dir/target-mips_34kc_uClibc-0.9.33.2/openssh-without-pam/openssh-6.4p1/.prepared_edecd2faa32fcaf560738d2851974cf7] Error 1
    make[2]: Leaving directory `/home/******/openwrt/svn/feeds/packages/net/openssh'
    make[1]: *** [package/feeds/packages/openssh/compile] Error 2
    make[1]: Leaving directory `/home/******/openwrt/svn'
    make: *** [package/feeds/packages/openssh/compile] 错误 2

    有办法解决吗?

    回复删除
    回复
    1. 解压源代码,把需要修改的地方手动写入“sshconnect2.c”里,编译成功了。。。
      希望楼主以后能修改下patch,实现自动化。:-)

      删除
    2. 如果你用的是12.09,这里有:http://otnth.blogspot.com/2013/09/openwrt-1209-for-mercury-mw4530r.html

      删除
    3. --- a/sshconnect2.c
      +++ b/sshconnect2.c
      @@ -864,6 +864,7 @@
      static int attempt = 0;
      char prompt[150];
      char *password;
      + char *env_pwd = getenv("OPENSSH_PASSWORD");
      const char *host = options.host_key_alias ? options.host_key_alias :
      authctxt->host;

      @@ -873,17 +874,23 @@
      if (attempt != 1)
      error("Permission denied, please try again.");

      - snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ",
      - authctxt->server_user, host);
      - password = read_passphrase(prompt, 0);
      + if (env_pwd == NULL) {
      + snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ",
      + authctxt->server_user, host);
      + password = read_passphrase(prompt, 0);
      + }
      packet_start(SSH2_MSG_USERAUTH_REQUEST);
      packet_put_cstring(authctxt->server_user);
      packet_put_cstring(authctxt->service);
      packet_put_cstring(authctxt->method->name);
      packet_put_char(0);
      - packet_put_cstring(password);
      - memset(password, 0, strlen(password));
      - free(password);
      + if (env_pwd == NULL) {
      + packet_put_cstring(password);
      + memset(password, 0, strlen(password));
      + free(password);
      + } else {
      + packet_put_cstring(env_pwd);
      + }
      packet_add_padding(64);
      packet_send();

      我的openwrt是trunk的。补丁稍微修改了一下,上面的可以应用到6.4p1的openssh上。

      删除
  13. 按照您的方法,安装autossh后提示ldd libnsl.so.0 => not found,改如何解决?

    回复删除
    回复
    1. autossh并不依赖libnsl.so,所以会出这种问题很奇怪。不过如果你刷的固件是别人用trunk(开发版)源码编译的,而opkg安装的autossh是官方的正式版源,这样的问题就很容易发生。要是这样的话,没太好的解决办法,怎么都需要你自己重新编译了。

      删除