一个Go语言SSH客户端的例子

an example of ssh client in golang

Google搜了好几个例子都用不了,好像是因为ssh库更新了,没办法只好自己摸索了。

这里用的是RSA Private Key来连接,要改成用密码连接也很容易。

参考文档:https://godoc.org/code.google.com/p/go.crypto/ssh

// 运行前得先安装ssh支持库: go get code.google.com/p/go.crypto/ssh

package main

import (
    "code.google.com/p/go.crypto/ssh"
    "io"
    "log"
)

var (
    pemBytes = `-----BEGIN RSA PRIVATE KEY-----
.......
.......
.......
-----END RSA PRIVATE KEY-----`
)

type keychain struct {
    signer ssh.Signer
}

func (k *keychain) Key(i int) (ssh.PublicKey, error) {
    if i != 0 {
        return nil, nil
    }
    pk := k.signer.PublicKey()
    return pk, nil
}

func (k *keychain) Sign(i int, rand io.Reader, data []byte) (sig []byte, err error) {
    return k.signer.Sign(rand, data)
}

func main() {
    signer, err := ssh.ParsePrivateKey([]byte(pemBytes))
    if err != nil {
        panic(err)
    }

    clientKey := &keychain{signer}

    config := &ssh.ClientConfig{
        User: "Hessian",
        Auth: []ssh.ClientAuth{
            ssh.ClientAuthKeyring(clientKey),
        },
    }
    c, err := ssh.Dial("tcp", "YOUR_HOST:22", config)
    if err != nil {
        log.Println("unable to dial remote side:", err)
    }
    defer c.Close()

    // Create a session
    session, err := c.NewSession()
    if err != nil {
        log.Fatalf("unable to create session: %s", err)
    }
    defer session.Close()

    b, err := session.Output("ls -l")
    if err != nil {
        log.Fatalf("failed to execute: %s", err)
    }
    log.Println("Output: ", string(b))

    return
}

Read: 2137

CentOS + Cacti 集中监控服务器 (笔记)

Cacti官方网站

官方文档——Installing Under Unix

Percona Monitoring Plugins (Plugins for Cacti)
Percona 为我们提供了很多包括linux,apache,mysql,redis,nginx等等常见服务的监控模板非常方便。
下载地址 | 官方文档

监控机配置
1. 下载Cacti
2. 解压到wwwroot目录(cacti默认URL是/cacti,不想改配置就同样解压到www/cacti好了)
3. 配置web服务(注意屏蔽cli目录访问)
4. 编译安装PHP SNMP扩展
5. 安装SNMP服务

yum install net-snmp net-snmp-libs net-snmp-utils

如何安装PHP SNMP扩展
进入PHP源代码目录下的ext/snmp目录,执行以下命令

phpize
./configure
make && make install

6. 修改cacti/include/config.php 配置mysql连接参数
7. 导入cacti.sql
8. 访问http://服务器地址/cati 开始安装

被监控机配置
1. 配置防火墙(打开UDP 161端口,其他端口根据需要打开)
对于运行在网络上的服务器来说对外开发的端口自然是越少越好,可能的话最好只对内部网络开放SNMP服务。
其他端口根据需要选择开放。
在/etc/sysconfig/iptables添加

-A INPUT -p udp -m state --state NEW -m udp -s 允许访问的IP --dport 161 -j ACCEPT

2. 安装SNMP服务

yum install net-snmp net-snmp-libs net-snmp-utils
service snmpd start

其他注意事项:
1. Percona的提供的脚本很多监控项目是通过ssh连接到远程服务器上,所以需要监控机有访问被监控机的权限,在Percona的脚本里是通过ssh密钥登陆的。
2. Percona默认访问的Nginx统计地址是/server-status,可以在cacti的data source里改或者偷懒直接改scripts/ss_get_by_ssh.php。
3. Percona为每种服务都提供了Host模板,在创建Host之后可以通过多次更改Host Template来合并不同的图表模板。当然,还是都分开比较好一点,都在一起太多了,乱。

待续。。。

Read: 1242

CentOS 6.4 openvpn服务器搭建笔记

OpenVPN 源代码、安装包下载地址

官方安装手册

官方easy-rsa3 github

1. 安装openvpn、easy-rsa
yum install openvpn easy-rsa

easy-rsa是生成秘钥用的一系列脚本集合。openvpn2.3版之后程序包里不再捆绑,需要单独安装,官方2.3版本的easy-rsa3改动比较
大,网上也很少有用easy-rsa3的文章,为了避免麻烦,直接装源里的easy-rsa就好了。

easy-rsa默认安装在/usr/share/easy-rsa/2.0/,为了方便我复制了一份到/etc/openvpn下面

cp -R /usr/share/easy-rsa/2.0 /etc/openvpn/easyrsa

2. 生成证书、秘钥

. ./vars
./build-key-server server
./build-key client

3. 配置openvpn

4. 配置iptables
#开启路由转发
echo 1 > /proc/sys/net/ipv4/ip_forward
# 使用NAT 让vpn客户端可以通过vpn服务器的私网IP 192.168.1.101 访问服务端私网内其他机器
-A POSTROUTING -s 10.8.0.0/24 -j SNAT –to-source 192.168.1.101

配置路由表
route add -net 192.168.1.0/24 dev eth1
route add -net default gw 192.168.1.1
etc. 使用 用户名/密码 验证的方法

参考文章:
《OpenVPN服务器配置》

《配置OpenVPN使用User/Pass方式验证登录》

《Iptables之FORWARD转发链》

《OpenVPN 2.0 HOWTO-局域网互访(翻译)》

《OpenVPN服务端架设配置说明》 (图文手册,简单明了)

Read: 282

*HACK* 为QT的TCP socket连接绑定本地IP地址

有时候我们会在机器上同时绑定多个IP地址(可能都是在一个网卡上),但是我们却无法指定程序使用哪个IP地址去建立连接。
虽然我之前的另一篇文章《在Windows/Linux下程序指定IP地址》讲过一种hack的办法,但是并不总是那么有效。就比如博主最近遇到的一个QT写的程序,不知道为什么就是bind不了,不过万幸的是我们有源代码。
通过修改QT框架的QNativeSocketEngine的实现我们还是可以bind到我们需要的IP地址的。

源代码文件:src/network/socket/qnativesocketengine.cpp

在大概410行的位置,在 d->socketType = socketType 之前添加以下代码

if (socketType == QAbstractSocket::TcpSocket && protocol == QAbstractSocket::IPv4Protocol) {
        char* bind_addr;
        if (bind_addr = getenv("BIND_ADDR")) {
            QHostAddress bind_hostaddr = QHostAddress(QString::fromLocal8Bit(bind_addr));

            if (!d->nativeBind(bind_hostaddr, 0)) {
                qDebug("BIND %s failed", bind_addr);
            }

            // bind之后状态是QAbstractSocket::BoundState,但是connectToHost要求必须是UnconnectedState
            d->socketState = QAbstractSocket::UnconnectedState;
        }
    }

使用方法

BIND_ADDR="你的IP地址" /path/to/your/program

好吧,用环境变量的办法当然不是那么美型,但是最直接有效。简单就是美啊。。。哈哈

Read: 1585

在Windows/Linux下让程序指定出口IP地址

Windows/Unix* 系统都支持为一个网卡绑定多个IP地址,但是通常操作系统会根据路由表自动选择IP地址,应用程序使用哪个IP地址用户无法主动控制。本文分别讲解在Linux和Windows下为应用程序绑定指定IP地址的方法。

关于Windows如何选择IP地址可以参考这篇文章:《Source IP address selection on a Multi-Homed Windows Computer》

一、 如何让Linux下的程序指定使用的IP地址

英文原文:《BINDING APPLICATIONS TO A SPECIFIC IP》

作者Daniel Ryde采用了LD_PRELOAD进行HACK,为应用程序注入一个动态库bind.so,这个动态库中对bind和connect函数加钩子,程序建立socket连接前绑定指定的本地IP地址。

使用方法:

Hessian:bind/ $ BIND_ADDR="192.168.8.9" LD_PRELOAD=./bind.so YOUR_PROGRAME

程序源码见文末

编译方法:

Hessian:bind/ $ gcc -nostartfiles -fpic -shared bind.c -o bind.so -ldl -D_GNU_SOURCE
Hessian:bind/ $ strip bind.so

二、 如何让Windows下的程序指定使用的IP地址

在Windows实现这个功能要相对麻烦一些,博主没有找到十分简单的办法,找了很久才发现这个高大上的东西——ForceBindIP – Bind any Windows application to a specific interface

程序原理就不翻译了,反正也没源码,不过大体跟linux版本的实现是差不多的,不过这边还多挂了WSA函数的钩子,覆盖的更完全。因为只会注入目标程序,如果网络访问是目标程序fork出去的进程发起的则不会受影响。

ForceBindIP works in two stages – the loader, ForceBindIP.exe will load the target application in a suspended state. It will then inject a DLL (BindIP.dll) which loads WS2_32.DLL into memory and intercepts the bind(), connect(), sendto(), WSAConnect() and WSASendTo() functions, redirecting them to code in the DLL which verifies which interface they will be bound to and if not the one specified, (re)binds the socket. Once the function intercepts are complete, the target application is resumed. Note that some applications with anti-debugger / injection techniques may not work correctly when an injected DLL is present; for the vast majority of applications though this technique should work fine.

作者声明支持的系统版本有:Windows NT/2000/XP/2003.

作者测试过可用的软件: DC++, uTorrent, Quake II, Quake III, Diablo II, StarCraft, Internet Explorer, Mozilla Firefox, Google Earth, Infantry, Real Player, Unreal Tournament 2004 (requires -i), Outlook 2000 (requires -i).

不可用的软件: GetRight (anti-debugger / forking techniques), WinCVS (forks cvs.exe)

博主测试过在Windows7上无法正常工作。搜狗浏览器也不知道是什么原因没有效果。

使用方法:

ForceBindIP 1.2.3.4 c:fullpathtoapp.exe

还可以通过网卡GUID进行绑定,GUID可以从注册表中找到[HKEY_LOCAL_MACHINESYSTEMCurrentControlSetServicesTcpipParametersInterfaces]

ForceBindIP {4FA65F75-7A5F-4BCA-A3A2-59824B2F5CA0} c:pathtoapp.exe

如果遇到程序崩溃或者什么意外情况可以尝试-i参数,这会让ForceBindIP等待目标程序进入它的消息循环后再注入DLL。

ForceBindIP -i 1.2.3.4 c:fullpathtoapp.exe

bind.c

/*
   Copyright (C) 2000  Daniel Ryde

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.
*/

/*
   LD_PRELOAD library to make bind and connect to use a virtual
   IP address as localaddress. Specified via the enviroment
   variable BIND_ADDR.

   Compile on Linux with:
   gcc -nostartfiles -fpic -shared bind.c -o bind.so -ldl -D_GNU_SOURCE


   Example in bash to make inetd only listen to the localhost
   lo interface, thus disabling remote connections and only
   enable to/from localhost:

   BIND_ADDR="127.0.0.1" LD_PRELOAD=./bind.so /sbin/inetd


   Example in bash to use your virtual IP as your outgoing
   sourceaddress for ircII:

   BIND_ADDR="your-virt-ip" LD_PRELOAD=./bind.so ircII

   Note that you have to set up your servers virtual IP first.


   This program was made by Daniel Ryde
   email: daniel@ryde.net
   web:   http://www.ryde.net/

   TODO: I would like to extend it to the accept calls too, like a
   general tcp-wrapper. Also like an junkbuster for web-banners.
   For libc5 you need to replace socklen_t with int.
*/



#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <dlfcn.h>
#include <errno.h>

int (*real_bind)(int, const struct sockaddr *, socklen_t);
int (*real_connect)(int, const struct sockaddr *, socklen_t);

char *bind_addr_env;
unsigned long int bind_addr_saddr;
unsigned long int inaddr_any_saddr;
struct sockaddr_in local_sockaddr_in[] = { 0 };

void _init (void)
{
	const char *err;

	real_bind = dlsym (RTLD_NEXT, "bind");
	if ((err = dlerror ()) != NULL) {
		fprintf (stderr, "dlsym (bind): %sn", err);
	}

	real_connect = dlsym (RTLD_NEXT, "connect");
	if ((err = dlerror ()) != NULL) {
		fprintf (stderr, "dlsym (connect): %sn", err);
	}

	inaddr_any_saddr = htonl (INADDR_ANY);
	if (bind_addr_env = getenv ("BIND_ADDR")) {
		bind_addr_saddr = inet_addr (bind_addr_env);
		local_sockaddr_in->sin_family = AF_INET;
		local_sockaddr_in->sin_addr.s_addr = bind_addr_saddr;
		local_sockaddr_in->sin_port = htons (0);
	}
}

int bind (int fd, const struct sockaddr *sk, socklen_t sl)
{
	static struct sockaddr_in *lsk_in;

	lsk_in = (struct sockaddr_in *)sk;
/*	printf("bind: %d %s:%dn", fd, inet_ntoa (lsk_in->sin_addr.s_addr),
		ntohs (lsk_in->sin_port));*/
        if ((lsk_in->sin_family == AF_INET)
		&& (lsk_in->sin_addr.s_addr == inaddr_any_saddr)
		&& (bind_addr_env)) {
		lsk_in->sin_addr.s_addr = bind_addr_saddr;
	}
	return real_bind (fd, sk, sl);
}

int connect (int fd, const struct sockaddr *sk, socklen_t sl)
{
	static struct sockaddr_in *rsk_in;
	
	rsk_in = (struct sockaddr_in *)sk;
/*	printf("connect: %d %s:%dn", fd, inet_ntoa (rsk_in->sin_addr.s_addr),
		ntohs (rsk_in->sin_port));*/
        if ((rsk_in->sin_family == AF_INET)
		&& (bind_addr_env)) {
		real_bind (fd, (struct sockaddr *)local_sockaddr_in, sizeof (struct sockaddr));
	}
	return real_connect (fd, sk, sl);
}

Read: 3065