開源精選》是我們分享Github、Gitee等開源社區中優質項目的欄目,包括技術、學習、實用與各種有趣的內容。本期推薦的是一個通過Web訪問設備終端的工具——rtty。
rtty由客戶端和服務端組成。客戶端采用純C實現,服務端采用GO語言實現,前端界面采用vue實現。使用rtty可以在任何地方通過Web訪問您的設備的終端,通過設備ID來區分您的不同的設備。rtty非常適合遠程維護Linux設備。
不支持 SSL:rtty(32K) + libev(56K)
支持的 SSL:+ libmbedtls(88K) + libmbedcrypto(241K) + libmbedx509(48k)
1 從Release頁面下載編譯好的二進制文件或者自己編譯
git clone https://github.com/zhaojh329/rttys
cd ui
npm install
npm run build
cd ..
./build.sh linux amd64
2 認證
生成一個 token:
$ rttys token
Please set a password:******
Your token is: 34762d07637276694b938d23f10d7164
使用 token:
$rttys -t 34762d07637276694b938d23f10d7164
您可以在配置文件中指定設備 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;
安裝依賴
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
將下面的參數替換為您自己的參數
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)來展示使用的技術。如果你還沒有讀過它,請先讀它。
記住流程:
對于根 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 。
我們來看一下那個 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 上的當前已驗證用戶。這個模塊就是這個用處。
在繼續這個頁面之前,我將寫一些與服務器 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}
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
*請認真填寫需求信息,我們會在24小時內與您取得聯系。