當前有一台Windows Server的文件服務器,為了方便文件的存儲和讀取,不想使用網頁(雲盤)的形式發布到公網,於是想到能否用WebDav的方式來掛載硬盤在本地電腦的文件管理器中。
接下來就是折騰過程的記錄
WebDAV (Web-based Distributed Authoring and Versioning) 一種基於HTTP1.1協議的通信協議。它擴展了HTTP 1.1,在GET、POST、HEAD等幾個HTTP標准方法以外添加了一些新的方法,使應用程序可對Web Server直接讀寫,並支持寫文件鎖定(Locking)及解鎖(Unlock),還可以支持文件的版本控制。
嘗試PHP
由於WebDAV就是一個Web Sever,於是首先想到的就是用PHP,也是由於用得順手。隨之而來的就是服務器使用Apache還是Nginx?最終選擇最簡答的PHPStudy懶人包,集成PHP所需要的環境。
使用PHP的一個缺點就是運行環境復雜,需要控制的點太多。Windows Firewall嘗試了很多次配置,都還是無法公網訪問網頁。於是逐漸放棄...
初見Caddy
緊接着開始了搜索簡便搭建WebDAV的方式,一個標題 “用Caddy搭建WebDAV服務器” 吸引住我了,一看只需幾行簡簡單單的配置就能開啟一個WebDav服務器,於是馬上上手。
Action speak louder than words!
在Caddy官網下載了運行文件,按照文中的配置,信心滿滿敲下命令,報錯了!!原來Caddy已經升級到v2版本了,而網上找的文章基本全是v1版本的教程。於是只能一步步慢慢讀官方 Document,終於將配置文件修改好了。
Caddy 2 is a powerful, enterprise-ready, open source web server with automatic HTTPS written in Go
在v1中WebDAV是一個內置的包,而在v2中需要在下載是勾選編譯,得到的可運行文件才是包含WebDAV的,且v2的WebDAV插件實現的功能更少了,也不是和v1一樣的維護者。對於authentication,在v2中已經使用哈希加密存儲的方式了。
# First Caddyfile for Webdav
:8080 {
encode gzip
basicauth / {
test XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
}
webdav /{
root E:\caddy
}
}
緊接着,測試時發現WebDAV正常運行,也可以連接,但是文件夾是鎖定無法讀寫的。當時也不知道咋回事,看了源碼也不懂於是陷入沉思。
Go實現WebDAV
突然發現源碼中其實也是importgolang.org/x/net/webdav
的庫,為何不能直接用Go來實現呢?經過一番努力搜索,出來了個初版。
package main
import (
"flag"
"fmt"
"golang.org/x/net/webdav"
"net/http"
)
func main() {
var addr *string
var path *string
addr = flag.String("addr", ":8080", "") // listen端口,默認8080
path = flag.String("path", ".", "") // 文件路徑,默認當前目錄
flag.Parse()
fmt.Println("addr=", *addr, ", path=", *path) // 在控制台輸出配置
http.ListenAndServe(*addr, &webdav.Handler{
FileSystem: webdav.Dir(*path),
LockSystem: webdav.NewMemLS(),
})
}
如此簡陋,沒有密碼、沒有運行錯誤判斷、沒有日志,But it works well! 因為沒有接觸過Go,完全不知道如何修改。又是一頓騷操作,密碼驗證的版本來了。
package main
import (
"flag"
"fmt"
"golang.org/x/net/webdav"
"net/http"
)
func main() {
var addr *string
var path *string
//
addr = flag.String("addr", ":8080", "")
path = flag.String("path", ".", "")
flag.Parse()
fs := &webdav.Handler{
FileSystem: webdav.Dir(*path),
LockSystem: webdav.NewMemLS(),
}
http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
// 獲取用戶名/密碼
username, password, ok := req.BasicAuth()
if !ok {
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
w.WriteHeader(http.StatusUnauthorized)
return
}
// 驗證用戶名/密碼
if username != "user" || password != "123456" {
http.Error(w, "WebDAV: need authorized!", http.StatusUnauthorized)
return
}
fs.ServeHTTP(w, req)
})
fmt.Println("addr=", *addr, ", path=", *path)
http.ListenAndServe(*addr, nil)
}
但是如果這樣的話,數據將是用HTTP在網絡明文上傳輸,這和裸奔有什么區別???安全還是很重要的,遂有了HTTPS版本。
package main
import (
"flag"
"fmt"
"net/http"
"os"
"golang.org/x/net/webdav"
)
var (
flagRootDir = flag.String("dir", "", "webdav root dir")
flagHttpAddr = flag.String("http", ":80", "http or https address")
flagHttpsMode = flag.Bool("https-mode", false, "use https mode")
flagCertFile = flag.String("https-cert-file", "cert.pem", "https cert file")
flagKeyFile = flag.String("https-key-file", "key.pem", "https key file")
flagUserName = flag.String("user", "", "user name")
flagPassword = flag.String("password", "", "user password")
flagReadonly = flag.Bool("read-only", false, "read only")
)
func init() {
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage of WebDAV Server\n")
flag.PrintDefaults()
}
}
func main() {
flag.Parse()
fs := &webdav.Handler{
FileSystem: webdav.Dir(*flagRootDir),
LockSystem: webdav.NewMemLS(),
}
http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
if *flagUserName != "" && *flagPassword != "" {
username, password, ok := req.BasicAuth()
if !ok {
w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
w.WriteHeader(http.StatusUnauthorized)
return
}
if username != *flagUserName || password != *flagPassword {
http.Error(w, "WebDAV: need authorized!", http.StatusUnauthorized)
return
}
}
if *flagReadonly {
switch req.Method {
case "PUT", "DELETE", "PROPPATCH", "MKCOL", "COPY", "MOVE":
http.Error(w, "WebDAV: Read Only!!!", http.StatusForbidden)
return
}
}
fs.ServeHTTP(w, req)
})
if *flagHttpsMode {
fmt.Println("HTTPS", "addr=", *flagHttpAddr, ", path=", *flagRootDir)
http.ListenAndServeTLS(*flagHttpAddr, *flagCertFile, *flagKeyFile, nil)
} else {
fmt.Println("HTTP", "addr=", *flagHttpAddr, ", path=", *flagRootDir)
http.ListenAndServe(*flagHttpAddr, nil)
}
}
光有HTTPS還需要有證書,於是又用OpenSSL給服務器生成了一個自證書,但是這個問題就導致在Windows上直接連接時一直報錯無法連接,由於不是可信任證書或者是無證書(證書生成和頒發這一塊不是很清楚,后續學習補充);但是可以使用RaiDrive
來正常掛載了😀。如下是運行命令及使用,已經基本可以滿足使用了。
E:\caddy>webdav_2.exe -dir E:\SDWAN -http :8080 -https-key-file ./ssl/server.key -https-cert-file ./ssl/server.crt -https-mode -user test -password test
E:\caddy>webdav_2.exe -help
Usage of WebDAV Server
-dir string
webdav root dir
-http string
http or https address (default ":80")
-https-cert-file string
https cert file (default "cert.pem")
-https-key-file string
https key file (default "key.pem")
-https-mode
use https mode
-password string
user password
-read-only
read only
-user string
user name
E:\caddy>
PPAP
I have a pen I have an apple ah Apple pen
I have a pen I have a pineapple ah Pineapples pen
Apple pen Pineapple pen ah Pen pineapple Apple Pen
查看Caddy是可以用於反向代理的,且Caddy可以自動免費注冊SSL證書,就差一個域名,恰巧Freenom可以白嫖域名,於是馬上開啟白嫖模式。(注意Freenom注冊的時候需要美國IP哦)
域名注冊好了,一切准備就緒,用Caddy做代理白嫖HTTPS,然后再運行一個最簡單的WebDAV,同時增加一個訪問日志記錄便於后期審計。PREFECT!
# Final Caddyfile
example.com:1234 {
encode gzip
basicauth /* {
test XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
}
reverse_proxy 127.0.0.1:8080
log {
output file ./log/access.log {
roll_size 1gb
roll_keep 20
}
format json
}
}
大功告成!