autossh Autossh是一个用于在不稳定网络环境下自动建立和维持SSH连接的工具。它可以监测SSH连接状态并在连接断开时自动重新连接,提供了更稳定和持续的连接。这对于需要长时间运行的SSH会话或需要持续传输数据的应用程序非常有用。
autossh的ssh的区别 Autossh是SSH的一个工具或扩展,用于在不稳定的网络环境中提供持久性和可靠性的SSH连接。因此,Autossh与SSH之间存在以下区别:
功能目的:SSH是一种网络协议,用于安全地进行远程登录和数据传输。它提供了加密和认证机制,允许用户在不安全的网络中建立安全的连接。Autossh则是在SSH基础上构建的工具,旨在提供自动重连和持久性连接功能,以应对不稳定的网络环境。
自动重连:SSH本身并不具备自动重连的功能。当SSH连接中断或网络连接不稳定时,通常需要手动重新建立连接。而Autossh则是专门设计用于自动检测连接状态并自动重新连接的工具。它能够在连接中断后自动尝试重新连接,确保持续的SSH连接。
隧道和端口转发:SSH具有隧道和端口转发功能,可以安全地转发网络流量和建立通信通道。Autossh也支持这些功能,并提供了方便的配置和管理选项,使得在不稳定网络环境中建立和维护这些隧道更加可靠和便捷。
平台和兼容性:SSH是一种网络协议,广泛支持各种操作系统和网络设备。Autossh作为一个工具,可以在多个平台上运行,包括Linux、Unix、Windows等。它是为了增强SSH连接的持久性和可靠性而开发的,不限于特定的操作系统或设备。
总之,SSH是一种网络协议,用于安全远程登录和数据传输,而Autossh是在SSH基础上提供自动重连和持久性连接功能的工具。Autossh可以在不稳定的网络环境中增强SSH连接的可靠性,并提供方便的隧道和端口转发管理选项。
1 从使用角度来讲autossh主要用于建立ssh通道,以实现内网穿透的功能。例如想要从外网可以访问到内网HTTP服务,外网服务器转发HTTP请求到内网服务器,这时就可以通过autossh建立ssh通道,然后通过http反向代理到ssh通道上,再通过ssh通道将请求转发到具体的内网端口和服务上面。但需要注意的是ssh本身就支持内网穿透以及建立ssh通道的功能,但ssh本身不支持监听通道端口以及重连功能,所以一旦出现网络异常之类的问题导致ssh通道断开,需要手动执行命令重连,这里autossh解决了ssh主要存在的问题(监听隧道端口以及自动重连)。
部署及使用 1 本身部署是极为简单的,只需要服务器端开启sshd服务,确保可以通过sshd连接,然后客户端下载autossh命令,通过autossh命令连接到服务器端即可。这里如果没有配置免密登录还需要输入密码,但一般情况需要配置免密登录(确保可以开机自启动该服务)。
autossh命令安装 1 2 3 4 5 centos: yum -y install epel-release #autossh在epel源里面 yum -y install autossh ubuntu: apt install autossh
命令使用 1 2 3 4 5 6 7 8 9 10 11 12 autossh [-V] [-M monitor_port[:echo_port]] [-f] [SSH_OPTIONS] 常用参数: -V:打印版本号 -M:指定监视端口号,用于监测SSH连接的状态。例如:-M 20000 (0表示禁用监听端口) -f:将Autossh放入后台运行。 -N:指定不执行远程命令。 -L:设置本地端口转发。例如:-L [本地地址:]本地端口:目标地址:目标端口。 -R:设置远程端口转发。例如:-R [远程地址:]远程端口:本地地址:本地端口。 -i:指定用于身份验证的私钥文件。 -p:指定SSH服务器的端口号。 -l:指定SSH连接的用户名。 -t:强制为SSH连接分配伪终端。
1 2 3 常用的命令格式为:autossh -M autossh监听端口(任意端口) -f -N -R A主机转发端口:B主机IP:B目的端口 root@A主机 。意为通过ssh连接到远程主机,并将其某端口转发到本地(该主机)的某端口。 例: autossh -p 30022 -M 55557 -CNR 19999:127.0.0.1:80 root@47.108.88.23 #连接ssh服务器47.108.88.23的30022端口,并将其19999端口转发到内网的80端口。55557是autossh监控端口。
实现外网访问内网HTTP服务(通过autossh) 这里主要讲解通过使用Autossh建立持久的SSH连接,并设置端口转发功能,可以实现外网对内网的HTTP服务进行安全访问。
在服务器上实现穿透 需要两台服务器,其中的服务器端需要有固定外网ip,客户端是内网服务器,通过下面的步骤来实现内外网穿透。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 服务器端操作: 1.启动sshd服务(一般都会默认安装),systemct start sshd启动它。 安装:yum -y install sshd 2.部署并启动nginx(或httpd)。 安装: yum -y install epel-release && yum -y install nginx 3.通过配置文件反向代理到ssh通道的端口上面(这里不做解释) 客户端操作: 1.部署安装autossh命令工具 yum -y install epel-release && yum -y install autossh 2.配置免密登录服务器(可选项,但如果是容器运行则为必选项) 首先ssh-keygen命令生成密钥,并将公钥文件里面的内容(cat /root/.ssh/id_rsa.pub查看)复制到服务器端的/root/.ssh/authorized_keys中去(如果没有需要在服务器生成) 3.使用autossh命令建立ssh通道。 执行命令: autossh -M autossh监听端口(任意端口) -f -N -R A主机转发端口:B主机IP:B目的端口 root@A主机 #本机为B主机,A主机为ssh服务器端 4.启动需要穿透访问的web服务(如web服务或nginx)。
在K8S上实现穿透 这里主要实现:以云服务器K8S的ingress为入口,将ingress的域名的访问请求代理至内网K8S的ingress中,然后通过内网ingress访问到具体服务。
1 2 3 4 5 6 7 准备工作: 1.首先需要确保内网的ingress服务以及ingress反向代理规则和后端服务运行正常(保证原有服务访问没问题),并确保云服务器的ingress正常可访问,将需要穿透的对应域名解析到云服务器的ingress。(这里不会对ingress做解释,需要保证ingress本身正常) 2.云服务器的ingress需要转发到具体容器pod中,也就是这里的ssh服务器端,下面会对该容器做详细解释。这里将该容器称为autossh-server 3.内网K8S中需要运行一个容器pod,用于作为autossh命令客户端与服务器建立通道,并将所有请求转发到内网ingress中,下面会对该容器做详细解释。这里将该容器称为autossh-client 实现的架构: 云服务器INGRESS-》autossh-server-》autossh-client-》内网INGRESS 其中autossh-server除了穿透需要的sshd服务以外,还需要nginx服务作为转发。autossh-client需要autossh命令服务实现穿透,还需要nginx服务,通过nginx转发到内网ingress,所以这里实际上是走了四个nginx的转发,但前两个和后两个本质上都是在K8S内网进行的转发,开销很小。
autossh-server 因为本身该需求在网上并没有较好的已实现的容器镜像,下面的容器都是手动编写制作的。这里需要先分析容器需要的功能。
1 首先这里autossh的服务器端需要安装sshd服务和nginx服务,在运行容器时需要将nginx的配置文件目录以及sshd的密钥目录挂载出来。最后通过yaml文件部署在k8s上。
Dockerfile 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 FROM centos:7 ENV TZ="Asia/Shanghai" RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone RUN yum install -y epel-release kde-l10n-Chinese glibc-common RUN yum install -y nginx crontabs RUN echo "0 0 * * * /bin/bash /nginx_cutlog.sh" >> /var/spool/cron/root RUN yum install -y openssh-server RUN localedef -c -f UTF-8 -i zh_CN zh_CN.utf8 ENV LC_ALL zh_CN.utf8 RUN mkdir /var/run/sshd RUN touch /etc/ssh/sshd_config && echo 'PermitRootLogin without-password' >> /etc/ssh/sshd_config && echo 'PubkeyAuthentication yes' >> /etc/ssh/sshd_config #禁止root密码登录且启用密钥登录 RUN ssh-keygen -t ecdsa -P '' -f /etc/ssh/ssh_host_ecdsa_key && ssh-keygen -t ed25519 -P '' -f /etc/ssh/ssh_host_ed25519_key && ssh-keygen -t rsa -P '' -f /etc/ssh/ssh_host_rsa_key #这三个是sshd默认的密钥传输使用的密钥文件 COPY startup.sh /home COPY nginx_cutlog.sh / EXPOSE 22 EXPOSE 80 CMD ["sh","/home/startup.sh"]
startup.sh 启动脚本
1 2 3 4 5 #!/bin/bash echo '启动nginx...' && nginx echo '启动crond...' && crond echo '启动sshd...' && /usr/sbin/sshd -D #这里启动了nginx、crontab、sshd,其中sshd以前台方式运行,因为docker运行必须要一个前台主进程。这里crontab主要是每日定时任务,执行脚本nginx_cutlog.sh,每日做日志切割以及删除一个月以前的日志文件。
nginx_cutlog.sh 改脚本通过cron每日执行,其作用为按天来切割nginx日志文件以及清除一个月之前的日志文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #!/bin/bash touch /tmp/1 log_dir="/var/log/nginx" current_date=$(date "+%Y-%m-%d") month_ago=$(date -d "-1 month" "+%Y-%m-%d") # 遍历目录下以.log结尾的文件 for file in "$log_dir"/*.log; do if [[ -f "$file" ]]; then # 获取文件的基本名称(不包含路径) base_name=$(basename "$file") # 检查文件名中是否已经包含日期 if [[ ! "$base_name" =~ [0-9]{4}-[0-9]{2}-[0-9]{2} ]]; then # 构造新的文件名,将日期追加到文件名中 new_file="${log_dir}/${base_name%.*}-$current_date.${base_name##*.}" # 切割文件 mv "$file" "$new_file" # 创建新的空日志文件 touch "$file" fi fi done # 遍历目录下以.log结尾的文件,并删除一个月以前的日志文件 find /var/log/nginx/ -mtime +30 -name "*.log" -exec rm -rf {} \;
autossh-server.yaml 部署文件,这里挂载了nginx配置文件目录,root目录(主要是.ssh存放公钥秘钥),nginx日志目录。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 apiVersion: apps/v1 #指定api版本标签 kind: StatefulSet #定义资源的类型/角色,ReplicaSet为控制器 metadata: #定义资源的元数据信息 name: autossh-server #定义资源的名称,在同一个namespace空间中必须是唯一的 labels: #定义资源标签 app: autossh-server spec: serviceName: autossh-server replicas: 1 #定义副本数量 selector: #定义选择器 matchLabels: #匹配上面的标签 tier: autossh-server #匹配模板名称 template: #定义模板 metadata: labels: tier: autossh-server spec: imagePullSecrets: - name: harborsecretkey containers: #定义容器信息 - name: autossh #容器名,与标签名要相同 imagePullPolicy: Always image: registry.cn-chengdu.aliyuncs.com/finsiot/centos-nginx:v1.0 #容器使用的镜像以及版本 ports: - containerPort: 80 - containerPort: 22 volumeMounts: - mountPath: /etc/nginx/conf.d/ name: autossh-server subPath: conf.d - mountPath: /root/ name: autossh-server subPath: root - mountPath: /var/log/nginx name: autossh-server subPath: log volumes: - name: autossh-server persistentVolumeClaim: claimName: autossh-nginx-conf --- apiVersion: v1 kind: Service metadata: name: autossh spec: type: NodePort selector: tier: autossh-server ports: - name: http #端口一 protocol: TCP port: 80 targetPort: 80 nodePort: 30080 - name: ssh protocol: TCP #端口二 port: 22 targetPort: 22 nodePort: 30022 --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: autossh-nginx-conf namespace: master spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi
1 2 镜像构建: 这里直接使用dockerbuild构建并push推送: docker build . -f Dockerfile -t docker-regisry.finsiot.com/finsiot/autossh-server:v1.0 && docker push docker-regisry.finsiot.com/finsiot/autossh-server:v1.0
autossh-client 1 client端这里首先需要autossh命令,然后还需要nginx服务,通过nginx来转发到k8s的ingress。
Dockerfile 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 FROM centos:7 USER root ENV TZ="Asia/Shanghai" RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone RUN yum install -y epel-release kde-l10n-Chinese glibc-common RUN yum install -y nginx crontabs RUN echo "0 0 * * * /bin/bash /nginx_cutlog.sh" >> /var/spool/cron/root RUN yum install -y autossh net-tools RUN localedef -c -f UTF-8 -i zh_CN zh_CN.utf8 ENV LC_ALL zh_CN.utf8 COPY startup.sh /home COPY nginx_cutlog.sh / CMD ["sh","/home/startup.sh"]
startup.sh 启动脚本
1 2 3 4 脚本说明: 1.启动脚本里面有四个必传变量$SERVER_SSH_PORT、$CLIENT_PORT、$CLIENT_PROXY_PORT、$SSH_SERVER,具体可以看脚本echo的注释,这里如果不传将无法正常启动。 2.脚本启动会生成秘钥文件id_rsa和其对应公钥文件id_rsa.pub,请将id_rsa.pub的公钥复制到对端服务器的authorized_keys文件中,以实现免密登录,这里规定了程序如果无法免密登录的话是无法正常运行的,因为容器启动没办法交互登录ssh。 3.请将/root或者/root/.ssh目录做持久化挂载,否则每次生成的秘钥都会改变。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #!/bin/bash if [[ -z "$SERVER_SSH_PORT" && -z "$CLIENT_PORT" && -z "$CLIENT_PROXY_PORT" && -z "$SSH_SERVER" ]]; then echo "未设置 \$CLIENT_PORT 或\$CLIENT_PROXY_PORT 或 \$SSH_SERVER" echo '$SERVER_SSH_PORT表示服务器端ssh的端口(端口号的具体数字,例如22)' echo '$CLIENT_PORT表示本地客户端的autossh监控端口(端口号的具体数字,例如22222)' echo '$CLIENT_PROXY_PORT表示远程端口转发到本地的ip及端口(例如8888:127.0.0.1:80,表示服务端的8888转发到本地80)' echo '$SSH_SERVER表示服务器端的用户名和IP地址(例如root@47.108.88.235)' exit 1 fi #判断keygen是否存在,不存在自动创建。 if [ ! -d "/root/.ssh" ] && [ ! -s "/root/.ssh/id_rsa" ] && [ ! -s "/root/.ssh/id_rsa.pub" ];then ssh-keygen -f /root/.ssh/id_rsa -P '' -q && echo 1 fi echo '下面是autossh客户端生成的公钥(id_rsa.pub),请将其复制到autossh服务器端的/root/.ssh/authorized_keys中,使其可以免密登录。' echo '========================' cat /root/.ssh/id_rsa.pub echo '========================' echo '这个ssh认证密钥公钥是服务(脚本)启动的时候生成了(位于/root/.ssh/id_rsa和id_rsa.pub),请务必要将/root/目录(或/root/.ssh目录)做数据持久化(目录挂载),以防止每次启动服务由于找不到之前的密钥文件就会重新生成密钥,其密钥(公钥)发生变化,从而无法免密登录。' nginx && echo 'nginx已启动,请务必将/etc/nginx/conf.d目录挂载为持久化存储(以免添加配置以后重启丢失nginx配置文件)' crond && echo 'crond已启动' #crontab守护进程,主要用于运行nginx日志切割定期清理的脚本。 echo '如果想要nginx(容器)重启后日志文件不丢失还需要将/var/log/nginx目录挂载为持久化存储。(可选项,非必须)' rm -rf /root/.ssh/known_hosts #每次启动时删除已信任的主机(autossh-server),容器运行的ssh服务状态容易变,例如更新的了容器之类的操作会使容器原本状态发生变化(变成新的主机),client端通过known_hosts中的主机是不能识别到新的主机的,这时会认证失败,所以这里为避免这个问题每次会删除该文件。 ssh -o StrictHostKeyChecking=no -o BatchMode=yes -p ${SERVER_SSH_PORT} ${SSH_SERVER} exit #每次启动前先用ssh命令连接一次,第一点是如果登录失败就直接结束服务,会提示先配置免密登录。第二点是这个ssh是免交互操作的(默认会提示是否保存主机,这里直接保存),如果这里不先连一次将其主机保存至known_hosts文件中的话,下面autossh连接时还会提示是否信任主机密钥的提示,但容器POD运行没有办法进行交互操作。 if [ ! $? -eq 0 ]; then echo "SSH登录失败,请检查免密登录设置" echo "=====提示:请务必配置好client端与server端的免密ssh登录配置再启动服务。=====" exit 1 fi echo '启动autossh服务.......' autossh -p ${SERVER_SSH_PORT} -M ${CLIENT_PORT} -CNR ${CLIENT_PROXY_PORT} ${SSH_SERVER} #该进程为容器前台进程
nginx_cutlog.sh 和上面autossh-server一样的日志切割及日志清除脚本,这里不多做解释。主要是client端也有个转发的nginx需要存日志。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #!/bin/bash touch /tmp/1 log_dir="/var/log/nginx" current_date=$(date "+%Y-%m-%d") month_ago=$(date -d "-1 month" "+%Y-%m-%d") # 遍历目录下以.log结尾的文件 for file in "$log_dir"/*.log; do if [[ -f "$file" ]]; then # 获取文件的基本名称(不包含路径) base_name=$(basename "$file") # 检查文件名中是否已经包含日期 if [[ ! "$base_name" =~ [0-9]{4}-[0-9]{2}-[0-9]{2} ]]; then # 构造新的文件名,将日期追加到文件名中 new_file="${log_dir}/${base_name%.*}-$current_date.${base_name##*.}" # 切割文件 mv "$file" "$new_file" # 创建新的空日志文件 touch "$file" fi fi done # 遍历目录下以.log结尾的文件,并删除一个月以前的日志文件 find /var/log/nginx/ -mtime +30 -name "*.log" -exec rm -rf {} \;
1 2 镜像构建: 这里直接使用dockerbuild构建并push推送: docker build . -f Dockerfile -t docker-regisry.finsiot.com/finsiot/autossh-client:v1.0 && docker push docker-regisry.finsiot.com/finsiot/autossh-client:v1.0
autossh-client.yaml 下面是部署文件,这里需要注意的是该容器启动了会打印公钥打日志上面,通过查看日志将公钥内容复制到autossh-server端的/root/.ssh/authorized_keys中,然后才能正常运行该服务。至于nginx的配置这里需要手动添加,该部署yaml文件已经将/etc/nginx/conf.d/做了挂载,反向代理的时候在配置文件的proxy_pass后面指定ingress的访问路径即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 apiVersion: v1 kind: ConfigMap metadata: name: autossh-client-config data: SERVER_SSH_PORT: '30022' CLIENT_PORT: '22222' CLIENT_PROXY_PORT: '9999:127.0.0.1:80' SSH_SERVER: 'root@47.108.88.235' --- apiVersion: apps/v1 #指定api版本标签 kind: StatefulSet #定义资源的类型/角色,ReplicaSet为控制器 metadata: #定义资源的元数据信息 name: autossh-client #定义资源的名称,在同一个namespace空间中必须是唯一的 labels: #定义资源标签 app: autossh-client spec: serviceName: autossh-client replicas: 1 #定义副本数量 selector: #定义选择器 matchLabels: #匹配上面的标签 tier: autossh-client #匹配模板名称 template: #定义模板 metadata: labels: tier: autossh-client spec: nodeName: node1 imagePullSecrets: - name: docker-registry containers: #定义容器信息 - name: autossh-client #容器名,与标签名要相同 imagePullPolicy: Always image: docker-registry.finsiot.com/finsiot/autossh-client:v1.0 #容器使用的镜像以及版本 envFrom: - configMapRef: name: autossh-client-config volumeMounts: - mountPath: /etc/nginx/conf.d/ name: autossh-client subPath: conf.d - mountPath: /root/ name: autossh-client subPath: root - mountPath: /var/log/nginx name: autossh-client subPath: log volumes: - name: autossh-client persistentVolumeClaim: claimName: autossh-client-pvc --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: autossh-client-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi
需要注意的地方 这里主要强调一下部署时还需要额外做的操作。
1.从使用上来讲需要注意在autossh-client启动后将其日志打印的公钥复制到server端(否则client无法成功启动)。
2.client和server端都需要手动加入nginx的反向代理配置文件(做域名转发配置),例如这里有一个www.test.com的域名,在server端需要配置反代转发到ssh通道的端口上面,而到了client端还需要将这个域名配置反代转发到内网ingress的ip或host上面。至于内网里面也需要一个www.test.com的域名指向对应的服务,而云服务器ingress也需要一个ingress指向autossh-server。
3.autossh-client端连接到server端的几个变量根据环境情况需要改。