-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
135 lines (111 loc) · 3.17 KB
/
main.go
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package main
import (
"crypto/ecdsa"
"crypto/sha256"
"crypto/subtle"
"embed"
"encoding/pem"
"flag"
"fmt"
"github.com/mpetavy/common"
"net/http"
"os"
"time"
)
var (
dir = flag.String("d", "", "directory to serve")
port = flag.Int("c", 1234, "port to serve the directory")
username = flag.String("u", "", "username")
password = flag.String("p", "", "password")
certFile *os.File
keyFile *os.File
)
//go:embed go.mod
var resources embed.FS
func init() {
common.Init("", "", "", "", "simple HTTPS download service", "", "", "", &resources, start, nil, nil, 0)
}
type application struct {
auth struct {
username string
password string
}
}
func start() error {
app := new(application)
app.auth.username = *username
app.auth.password = *password
mux := http.NewServeMux()
mux.HandleFunc("/", app.basicAuth(http.FileServer(http.Dir(*dir)).ServeHTTP))
srv := &http.Server{
Addr: fmt.Sprintf(":%d", *port),
Handler: mux,
IdleTimeout: time.Minute,
ReadTimeout: 10 * time.Second,
WriteTimeout: 30 * time.Second,
}
tlsConfig, err := common.NewTlsConfigFromFlags()
if common.Error(err) {
return err
}
certPEM := common.CertificateAsPEM(&tlsConfig.Certificates[0])
certBytes, _ := pem.Decode(certPEM)
if certBytes == nil {
return fmt.Errorf("cannot find PEM block with certificate")
}
certFile, err = common.CreateTempFile()
if common.Error(err) {
return err
}
err = os.WriteFile(certFile.Name(), certPEM, common.DefaultFileMode)
if common.Error(err) {
return err
}
keyPEM, err := common.PrivateKeyAsPEM(tlsConfig.Certificates[0].PrivateKey.(*ecdsa.PrivateKey))
if common.Error(err) {
return err
}
keyBytes, _ := pem.Decode(keyPEM)
if keyBytes == nil {
return fmt.Errorf("cannot find PEM block with key")
}
keyFile, err = common.CreateTempFile()
if common.Error(err) {
return err
}
err = os.WriteFile(keyFile.Name(), keyPEM, common.DefaultFileMode)
if common.Error(err) {
return err
}
common.Info("starting server on %s", srv.Addr)
err = srv.ListenAndServeTLS(certFile.Name(), keyFile.Name())
if common.Error(err) {
return err
}
return nil
}
func (app *application) basicAuth(next http.HandlerFunc) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
username, password, ok := r.BasicAuth()
if ok {
usernameHash := sha256.Sum256([]byte(username))
passwordHash := sha256.Sum256([]byte(password))
expectedUsernameHash := sha256.Sum256([]byte(app.auth.username))
expectedPasswordHash := sha256.Sum256([]byte(app.auth.password))
usernameMatch := (subtle.ConstantTimeCompare(usernameHash[:], expectedUsernameHash[:]) == 1)
passwordMatch := (subtle.ConstantTimeCompare(passwordHash[:], expectedPasswordHash[:]) == 1)
if usernameMatch && passwordMatch {
common.Info("Successful login: %s %s", r.RemoteAddr, app.auth.username)
next.ServeHTTP(w, r)
return
} else {
common.Warn("Unsuccessful login: %s %s", r.RemoteAddr, app.auth.username)
}
}
w.Header().Set("WWW-Authenticate", `Basic realm="restricted", charset="UTF-8"`)
http.Error(w, "Unauthorized", http.StatusUnauthorized)
})
}
func main() {
common.Run([]string{"d", "u", "p"})
}