整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

运维神器!一个可以通过Web访问Linux终端的工具

运维神器!一个可以通过Web访问Linux终端的工具

开源精选》是我们分享Github、Gitee等开源社区中优质项目的栏目,包括技术、学习、实用与各种有趣的内容。本期推荐的是一个通过Web访问设备终端的工具——rtty。

rtty由客户端和服务端组成。客户端采用纯C实现,服务端采用GO语言实现,前端界面采用vue实现。使用rtty可以在任何地方通过Web访问您的设备的终端,通过设备ID来区分您的不同的设备。rtty非常适合远程维护Linux设备。


特性

  • 客户端 C 语言实现,非常小,适合嵌入式 Linux

不支持 SSL:rtty(32K) + libev(56K)

支持的 SSL:+ libmbedtls(88K) + libmbedcrypto(241K) + libmbedx509(48k)

  • 远程批量执行命令
  • 支持SSL:openssl, mbedtls, CyaSSl(wolfssl)
  • SSL 双向认证(mTLS)
  • 非常方便地上传和下载文件
  • 根据设备ID访问不同的设备
  • 支持 HTTP 代理 - 访问您的设备的 Web
  • 基于Xterm.js的全功能终端
  • 部署简单,使用方便


客户端依赖

  • libev - 高性能的事件循环库
  • mbedtls(polarssl)CyaSSl(wolfssl)或者openssl - 如果您需要支持SSL

部署服务端

1 从Release页面下载编译好的二进制文件或者自己编译

git clone https://github.com/zhaojh329/rttys

cd ui
npm install
npm run build
cd ..

./build.sh linux amd64

2 认证

  • Token

生成一个 token:

$ rttys token
Please set a password:******
Your token is: 34762d07637276694b938d23f10d7164

使用 token:

$rttys -t 34762d07637276694b938d23f10d7164
  • SSL 双向认证(mTLS)

您可以在配置文件中指定设备 CA 存储(有效文件)或在 CLI 中指定设备 CA 存储(参数 ssl-cacert) 来启用 mTLS。 存储中没有有效 CA 的设备将在 TLS 握手中断开连接。

3 运行服务

移动rttys可执行程序到/usr/local/bin/

sudo mv rttys /usr/local/bin/

拷贝配置文件到/etc/rttys/

sudo mkdir /etc/rttys
sudo cp rttys.conf /etc/rttys/

创建一个systemd单元文件: /etc/systemd/system/rttys.service

[Unit]
Description=rttys
After=network.target

[Service]
ExecStart=/usr/local/bin/rttys run -c /etc/rttys/rttys.conf
TimeoutStopSec=5s

[Install]
WantedBy=multi-user.target

要首次启动该服务,请执行通常的systemctl操作

sudo systemctl daemon-reload
sudo systemctl enable rttys
sudo systemctl start rttys

可以通过以下方式停止服务

sudo systemctl stop rttys

4 数据库准备(Sqlite、MySql 或者 Mariadb)

在数据库实例上,以root用户身份登录到数据库控制台

mysql -u root -p

创建将由 Rttys 使用的数据库用户,通过密码验证。本例使用 “rttys” 作为密码。请为您的实例使用安全密码

CREATE USER 'rttys' IDENTIFIED BY 'rttys';

使用 UTF-8 字符集和排序规则创建数据库。确保使用 utf8mb4 字符集而不是 utf8,因为前者支持基本多语言平面 之外的所有 Unicode字符(包括emojis)。另外,根据您期望的内容选择排序规则。如有疑问,请使用 unicode_ci 或general_ci

CREATE DATABASE rttys CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_unicode_ci';

将数据库上的所有权限授予上面创建的数据库用户

GRANT ALL PRIVILEGES ON rttys.* TO 'rttys';
FLUSH PRIVILEGES;


安装rtty(针对Linux发行版)

安装依赖

sudo apt install -y libev-dev libssl-dev      # Ubuntu, Debian
sudo pacman -S --noconfirm libev openssl      # ArchLinux
sudo yum install -y libev-devel openssl-devel # Centos

克隆rtty代码

git clone --recursive https://github.com/zhaojh329/rtty.git

编译

cd rtty && mkdir build && cd build
cmake .. && make install

运行rtty

将下面的参数替换为您自己的参数

sudo rtty -I 'My-device-ID' -h 'your-server' -p 5912 -a -v -d 'My Device Description'

如果您的 rttys 配置了一个 token,请加上如下参数(将下面的 token 替换为您自己生成的)

-t 34762d07637276694b938d23f10d7164

通过浏览器访问

使用 Web 浏览器访问您的服务器: http://your-server-host:5913,然后点击连接按钮。

或者直接连接设备,无需 Web 登录(需要在服务端配置设备白名单)

http://your-server-host:5913/connect/devid1

http://your-server-host:5913/connect/devid2

从本地传输文件到远程设备

rtty -R

从远程设备传输文件到本地

rtty -S test.txt


—END—

开源协议:MIT

开源地址:https://github.com/zhaojh329/rtty

译自: https://nicolasparada.netlify.com/posts/passwordless-auth-client/

作者: Nicolás Parada

译者: qhwdw

我们继续 无密码验证 的文章。上一篇文章中,我们用 Go 写了一个 HTTP 服务,用这个服务来做无密码验证 API。今天,我们为它再写一个 JavaScript 客户端。

我们将使用 这里的 这个单页面应用程序(SPA)来展示使用的技术。如果你还没有读过它,请先读它。

记住流程:

  • 用户输入其 email。
  • 用户收到一个带有魔法链接的邮件。
  • 用户点击该链接、
  • 用户验证成功。

对于根 URL(/),我们将根据验证的状态分别使用两个不同的页面:一个是带有访问表单的页面,或者是已验证通过的用户的欢迎页面。另一个页面是验证回调的重定向页面。

伺服

我们将使用相同的 Go 服务器来为客户端提供服务,因此,在我们前面的 main.go 中添加一些路由:

router.Handle("GET", "/...", http.FileServer(SPAFileSystem{http.Dir("static")}))

type SPAFileSystem struct {

fs http.FileSystem

}

func (spa SPAFileSystem) Open(name string) (http.File, error) {

f, err :=spa.fs.Open(name)

if err !=nil {

return spa.fs.Open("index.html")

}

return f, nil

}

这个伺服文件放在 static 下,配合 static/index.html 作为回调。

你可以使用你自己的服务器,但是你得在服务器上启用 CORS 。

HTML

我们来看一下那个 static/index.html 文件。

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<title>Passwordless Demo</title>

<link rel="shortcut icon" href="data:,">

<script src="/js/main.js" type="module"></script>

</head>

<body></body>

</html>

单页面应用程序的所有渲染由 JavaScript 来完成,因此,我们使用了一个空的 body 部分和一个 main.js 文件。

我们将使用 上篇文章 中的 Router。

渲染

现在,我们使用下面的内容来创建一个 static/js/main.js 文件:

import Router from 'https://unpkg.com/@nicolasparada/router'

import { isAuthenticated } from './auth.js'

const router=new Router()

router.handle('/', guard(view('home')))

router.handle('/callback', view('callback'))

router.handle(/^\//, view('not-found'))

router.install(async resultPromise=> {

document.body.innerHTML=''

document.body.appendChild(await resultPromise)

})

function view(name) {

return (...args)=> import(`/js/pages/${name}-page.js`)

.then(m=> m.default(...args))

}

function guard(fn1, fn2=view('welcome')) {

return (...args)=> isAuthenticated()

? fn1(...args)

: fn2(...args)

}

与上篇文章不同的是,我们实现了一个 isAuthenticated() 函数和一个 guard() 函数,使用它去渲染两种验证状态的页面。因此,当用户访问 / 时,它将根据用户是否通过了验证来展示主页或者是欢迎页面。

验证

现在,我们来编写 isAuthenticated() 函数。使用下面的内容来创建一个 static/js/auth.js 文件:

export function getAuthUser() {

const authUserItem=localStorage.getItem('auth_user')

const expiresAtItem=localStorage.getItem('expires_at')

if (authUserItem !==null && expiresAtItem !==null) {

const expiresAt=new Date(expiresAtItem)

if (!isNaN(expiresAt.valueOf()) && expiresAt > new Date()) {

try {

return JSON.parse(authUserItem)

} catch (_) { }

}

}

return null

}

export function isAuthenticated() {

return localStorage.getItem('jwt') !==null && getAuthUser() !==null

}

当有人登入时,我们将保存 JSON 格式的 web 令牌、它的过期日期,以及在 localStorage 上的当前已验证用户。这个模块就是这个用处。

  • getAuthUser() 用于从 localStorage 获取已认证的用户,以确认 JSON 格式的 Web 令牌没有过期。
  • isAuthenticated() 在前面的函数中用于去检查它是否没有返回 null。

获取

在继续这个页面之前,我将写一些与服务器 API 一起使用的 HTTP 工具。

我们使用以下的内容去创建一个 static/js/http.js 文件:

import { isAuthenticated } from './auth.js'

function get(url, headers) {

return fetch(url, {

headers: Object.assign(getAuthHeader(), headers),

}).then(handleResponse)

}

function post(url, body, headers) {

return fetch(url, {

method: 'POST',

headers: Object.assign(getAuthHeader(), { 'content-type': 'application/json' }, headers),

body: JSON.stringify(body),

}).then(handleResponse)

}

function getAuthHeader() {

return isAuthenticated()

? { authorization: `Bearer ${localStorage.getItem('jwt')}` }

: {}

}

export async function handleResponse(res) {

const body=await res.clone().json().catch(()=> res.text())

const response={

statusCode: res.status,

statusText: res.statusText,

headers: res.headers,

body,

}

if (!res.ok) {

const message=typeof body==='object' && body !==null && 'message' in body

? body.message

: typeof body==='string' && body !==''

? body

: res.statusText

const err=new Error(message)

throw Object.assign(err, response)

}

return response

}

export default {

get,

post,

}

这个模块导出了 get() 和 post() 函数。它们是 fetch API 的封装。当用户是已验证的,这二个函数注入一个 Authorization: Bearer <token_here> 头到请求中;这样服务器就能对我们进行身份验证。

欢迎页

我们现在来到欢迎页面。用如下的内容创建一个 static/js/pages/welcome-page.js 文件:

const template=document.createElement('template')

template.innerHTML=`

<h1>Passwordless Demo</h1>

<h2>Access</h2>

<form id="access-form">

<input type="email" placeholder="Email" autofocus required>

<button type="submit">Send Magic Link</button>

</form>

`

export default function welcomePage() {

const page=template.content.cloneNode(true)

page.getElementById('access-form')

.addEventListener('submit', onAccessFormSubmit)

return page

}

这个页面使用一个 HTMLTemplateElement 作为视图。这只是一个输入用户 email 的简单表单。

为了避免干扰,我将跳过错误处理部分,只是将它们输出到控制台上。

现在,我们来写 onAccessFormSubmit() 函数。

import http from '../http.js'

function onAccessFormSubmit(ev) {

ev.preventDefault()

const form=ev.currentTarget

const input=form.querySelector('input')

const email=input.value

sendMagicLink(email).catch(err=> {

console.error(err)

if (err.statusCode===404 && wantToCreateAccount()) {

runCreateUserProgram(email)

}

})

}

function sendMagicLink(email) {

return http.post('/api/passwordless/start', {

email,

redirectUri: location.origin + '/callback',

}).then(()=> {

alert('Magic link sent. Go check your email inbox.')

})

}

function wantToCreateAccount() {

return prompt('No user found. Do you want to create an account?')

}

它对 /api/passwordless/start 发起了 POST 请求,请求体中包含 email 和 redirectUri。在本例中它返回 404 Not Found 状态码时,我们将创建一个用户。

function runCreateUserProgram(email) {

const username=prompt("Enter username")

if (username===null) return

http.post('/api/users', { email, username })

.then(res=> res.body)

.then(user=> sendMagicLink(user.email))

.catch(console.error)

}

这个用户创建程序,首先询问用户名,然后使用 email 和用户名做一个 POST 请求到 /api/users。成功之后,给创建的用户发送一个魔法链接。

回调页

这是访问表单的全部功能,现在我们来做回调页面。使用如下的内容来创建一个 static/js/pages/callback-page.js 文件:

import http from '../http.js'

const template=document.createElement('template')

template.innerHTML=`

<h1>Authenticating you</h1>

`

export default function callbackPage() {

const page=template.content.cloneNode(true)

const hash=location.hash.substr(1)

const fragment=new URLSearchParams(hash)

for (const [k, v] of fragment.entries()) {

fragment.set(decodeURIComponent(k), decodeURIComponent(v))

}

const jwt=fragment.get('jwt')

const expiresAt=fragment.get('expires_at')

http.get('/api/auth_user', { authorization: `Bearer ${jwt}` })

.then(res=> res.body)

.then(authUser=> {

localStorage.setItem('jwt', jwt)

localStorage.setItem('auth_user', JSON.stringify(authUser))

localStorage.setItem('expires_at', expiresAt)

location.replace('/')

})

.catch(console.error)

return page

}

请记住……当点击魔法链接时,我们会来到 /api/passwordless/verify_redirect,它将把我们重定向到重定向 URI,我们将放在哈希中的 JWT 和过期日期传递给 /callback。

回调页面解码 URL 中的哈希,提取这些参数去做一个 GET 请求到 /api/auth_user,用 JWT 保存所有数据到 localStorage 中。最后,重定向到主页面。

主页

创建如下内容的 static/pages/home-page.js 文件:

import { getAuthUser } from '../auth.js'

export default function homePage() {

const authUser=getAuthUser()

const template=document.createElement('template')

template.innerHTML=`

<h1>Passwordless Demo</h1>

<p>Welcome back, ${authUser.username}

点击“了解更多”可访问文内链接

ginx 安装配置

Nginx("engine x")是一款是由俄罗斯的程序设计师Igor Sysoev所开发高性能的 Web和 反向代理 服务器,也是一个 IMAP/POP3/SMTP 代理服务器。

在高连接并发的情况下,Nginx是Apache服务器不错的替代品。


Nginx 安装

系统平台:CentOS release 6.6 (Final) 64位。

一、安装编译工具及库文件

yum -y install make zlib zlib-devel gcc-c++ libtool openssl openssl-devel

二、首先要安装 PCRE

PCRE 作用是让 Nginx 支持 Rewrite 功能。

1、下载 PCRE 安装包,下载地址: http://downloads.sourceforge.net/project/pcre/pcre/8.35/pcre-8.35.tar.gz

[root@bogon src]# wget http://downloads.sourceforge.net/project/pcre/pcre/8.35/pcre-8.35.tar.gz

2、解压安装包:

[root@bogon src]# tar zxvf pcre-8.35.tar.gz

3、进入安装包目录

[root@bogon src]# cd pcre-8.35

4、编译安装

[root@bogon pcre-8.35]# ./configure[root@bogon pcre-8.35]# make && make install

5、查看pcre版本

[root@bogon pcre-8.35]# pcre-config --version

安装 Nginx

1、下载 Nginx,下载地址:http://nginx.org/download/nginx-1.6.2.tar.gz

[root@bogon src]# wget http://nginx.org/download/nginx-1.6.2.tar.gz

2、解压安装包

[root@bogon src]# tar zxvf nginx-1.6.2.tar.gz

3、进入安装包目录

[root@bogon src]# cd nginx-1.6.2

4、编译安装

[root@bogon nginx-1.6.2]# ./configure --prefix=/usr/local/webserver/nginx --with-http_stub_status_module --with-http_ssl_module --with-pcre=/usr/local/src/pcre-8.35[root@bogon nginx-1.6.2]# make[root@bogon nginx-1.6.2]# make install

5、查看nginx版本

[root@bogon nginx-1.6.2]# /usr/local/webserver/nginx/sbin/nginx -v

到此,nginx安装完成。


Nginx 配置

创建 Nginx 运行使用的用户 www:

[root@bogon conf]# /usr/sbin/groupadd www [root@bogon conf]# /usr/sbin/useradd -g www www

配置nginx.conf ,将/usr/local/webserver/nginx/conf/nginx.conf替换为以下内容

[root@bogon conf]# cat /usr/local/webserver/nginx/conf/nginx.conf
user www www;worker_processes 2; #设置值和CPU核心数一致error_log /usr/local/webserver/nginx/logs/nginx_error.log crit; #日志位置和日志级别pid /usr/local/webserver/nginx/nginx.pid;#Specifies the value for maximum file descriptors that can be opened by this process.worker_rlimit_nofile 65535;events{ use epoll; worker_connections 65535;}http{ include mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" $http_x_forwarded_for'; #charset gb2312; 
 server_names_hash_bucket_size 128; client_header_buffer_size 32k; large_client_header_buffers 4 32k; client_max_body_size 8m; 
 sendfile on; tcp_nopush on; keepalive_timeout 60; tcp_nodelay on; fastcgi_connect_timeout 300; fastcgi_send_timeout 300; fastcgi_read_timeout 300; fastcgi_buffer_size 64k; fastcgi_buffers 4 64k; fastcgi_busy_buffers_size 128k; fastcgi_temp_file_write_size 128k; gzip on; 
 gzip_min_length 1k; gzip_buffers 4 16k; gzip_http_version 1.0; gzip_comp_level 2; gzip_types text/plain application/x-javascript text/css application/xml; gzip_vary on; 
 #limit_zone crawler $binary_remote_addr 10m; #下面是server虚拟主机的配置 server
 { listen 80;#监听端口 server_name localhost;#域名 index index.html index.htm index.php; root /usr/local/webserver/nginx/html;#站点目录 location ~ .*\.(php|php5)?$
 { #fastcgi_pass unix:/tmp/php-cgi.sock; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; include fastcgi.conf; } location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|ico)$
 { expires 30d; # access_log off; } location ~ .*\.(js|css)?$
 { expires 15d; # access_log off; } access_log off; }}

检查配置文件ngnix.conf的正确性命令:

[root@bogon conf]# /usr/local/webserver/nginx/sbin/nginx -t


启动 Nginx

Nginx 启动命令如下:

[root@bogon conf]# /usr/local/webserver/nginx/sbin/nginx


访问站点

从浏览器访问我们配置的站点ip:


Nginx 其他命令

以下包含了 Nginx 常用的几个命令:

/usr/local/webserver/nginx/sbin/nginx -s reload # 重新载入配置文件/usr/local/webserver/nginx/sbin/nginx -s reopen # 重启 Nginx/usr/local/webserver/nginx/sbin/nginx -s stop # 停止 Nginx