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 条评论 :

zhuYe 说...

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

oCameLo 说...

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

Unknown 说...

请教一下,我的openwrt没有/opt/目录怎么安装呢?

oCameLo 说...

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

Unknown 说...

谢谢了,我试试

lizhu 说...

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

lizhu 说...
此评论已被作者删除。
lizhu 说...
此评论已被作者删除。
lizhu 说...
此评论已被作者删除。
oCameLo 说...

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

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

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

lizhu 说...
此评论已被作者删除。
lizhu 说...
此评论已被作者删除。
lizhu 说...
此评论已被作者删除。
70599 说...

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

谢谢!

oCameLo 说...

我写在了/etc/init.d/autossh,OPENSSH_PASSWORD就是

70599 说...
此评论已被作者删除。
70599 说...

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

不懂代码,不好意思呀!

oCameLo 说...

不是…

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

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

70599 说...

谢谢你!终于搞好啦!

匿名 说...

你好,能不能发一个12.09的版本啊。谢谢

oCameLo 说...
此评论已被博客管理员删除。
oCaemLo 说...

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

匿名 说...

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

70599 说...

最近在编译的时候输出如下错误:
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

有办法解决吗?

70599 说...

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

oCameLo 说...

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

70599 说...

--- 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上。

xueflaing 说...

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

oCameLo 说...

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