Remove TLSConfig fields
This commit is contained in:
parent
ec68ab8609
commit
ace3e682de
32
README.md
32
README.md
@ -32,9 +32,8 @@ A quick overview of the Gemini protocol:
|
|||||||
|
|
||||||
The way this is implemented in this package is like so:
|
The way this is implemented in this package is like so:
|
||||||
|
|
||||||
1. Client makes a request with `NewRequest`. The client can verify server
|
1. Client makes a request with `NewRequest`. The client then sends the request
|
||||||
certificates in the Request options, see [Recommended TLS
|
with `Do(*Request) (*Response, error)`.
|
||||||
configuration](#recommended-tls-configuration).
|
|
||||||
2. Server recieves the request and constructs a response.
|
2. Server recieves the request and constructs a response.
|
||||||
The server calls the `Serve(*ResponseWriter, *Request)` method on the
|
The server calls the `Serve(*ResponseWriter, *Request)` method on the
|
||||||
`Handler` field. The handler writes the response. The server then closes
|
`Handler` field. The handler writes the response. The server then closes
|
||||||
@ -42,30 +41,3 @@ The way this is implemented in this package is like so:
|
|||||||
5. Client recieves the response as a `*Response`. The client then handles the
|
5. Client recieves the response as a `*Response`. The client then handles the
|
||||||
response. The client can now verify the certificate of the server using a
|
response. The client can now verify the certificate of the server using a
|
||||||
Trust-On-First-Use method.
|
Trust-On-First-Use method.
|
||||||
|
|
||||||
## Recommended TLS configuration
|
|
||||||
|
|
||||||
For clients, the recommended TLS configuration is as follows:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Accept self-signed server certificates
|
|
||||||
req.TLSConfig.InsecureSkipVerify = true
|
|
||||||
// Manually verify server certificates, using TOFU
|
|
||||||
req.TLSConfig.VerifyPeerCertificate = func(rawCerts [][]byte, chains [][]*x509.Certificate) error {
|
|
||||||
// Verify the server certificate here
|
|
||||||
// Return an error on failure, or nil on success
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that `gemini.Get` does not verify server certificates.
|
|
||||||
|
|
||||||
For servers, the recommended TLS configuration is as follows:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Specify a certificate
|
|
||||||
// To load a certificate, use `tls.LoadX509KeyPair`.
|
|
||||||
srv.TLSConfig.Certificates = append(srv.TLSConfig.Certificates, cert)
|
|
||||||
// Request client certificates
|
|
||||||
srv.TLSConfig.ClientAuth = tls.RequestClientCert
|
|
||||||
```
|
|
||||||
|
@ -34,8 +34,7 @@ func makeRequest(url string) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
req.TLSConfig.InsecureSkipVerify = true
|
req.Certificate = cert
|
||||||
req.TLSConfig.Certificates = append(req.TLSConfig.Certificates, cert)
|
|
||||||
resp, err := gemini.Do(req)
|
resp, err := gemini.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
@ -63,9 +62,9 @@ func makeRequest(url string) {
|
|||||||
case gemini.StatusClassPermanentFailure:
|
case gemini.StatusClassPermanentFailure:
|
||||||
log.Fatal("Permanent failure")
|
log.Fatal("Permanent failure")
|
||||||
case gemini.StatusClassClientCertificateRequired:
|
case gemini.StatusClassClientCertificateRequired:
|
||||||
log.Fatal("Client Certificate Required")
|
log.Fatal("Client certificate required")
|
||||||
default:
|
default:
|
||||||
log.Fatal("Protocol Error")
|
log.Fatal("Protocol error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"git.sr.ht/~adnano/go-gemini"
|
"git.sr.ht/~adnano/go-gemini"
|
||||||
@ -18,27 +17,24 @@ func main() {
|
|||||||
// openssl ecparam -genkey -name secp384r1 -out server.key
|
// openssl ecparam -genkey -name secp384r1 -out server.key
|
||||||
// openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
|
// openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
|
||||||
//
|
//
|
||||||
config := tls.Config{}
|
|
||||||
cert, err := tls.LoadX509KeyPair("examples/server/server.crt", "examples/server/server.key")
|
cert, err := tls.LoadX509KeyPair("examples/server/server.crt", "examples/server/server.key")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
config.Certificates = append(config.Certificates, cert)
|
|
||||||
config.ClientAuth = tls.RequestClientCert
|
|
||||||
config.VerifyPeerCertificate = func(rawCerts [][]byte, chains [][]*x509.Certificate) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
mux := &gemini.ServeMux{}
|
mux := &gemini.ServeMux{}
|
||||||
mux.HandleFunc("/", func(rw *gemini.ResponseWriter, req *gemini.Request) {
|
mux.HandleFunc("/", func(rw *gemini.ResponseWriter, req *gemini.Request) {
|
||||||
log.Printf("Request from %s for %s with certificates %v", req.RemoteAddr.String(), req.URL.String(), req.TLS.PeerCertificates)
|
|
||||||
rw.WriteHeader(gemini.StatusSuccess, "text/gemini")
|
rw.WriteHeader(gemini.StatusSuccess, "text/gemini")
|
||||||
rw.Write([]byte("You requested " + req.URL.String()))
|
rw.Write([]byte("You requested " + req.URL.String()))
|
||||||
|
log.Printf("Request from %s for %s", req.RemoteAddr.String(), req.URL)
|
||||||
|
if len(req.TLS.PeerCertificates) != 0 {
|
||||||
|
log.Print("Client certificate: ", gemini.Fingerprint(req.TLS.PeerCertificates[0]))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
server := gemini.Server{
|
server := gemini.Server{
|
||||||
TLSConfig: config,
|
|
||||||
Handler: mux,
|
Handler: mux,
|
||||||
|
Certificate: cert,
|
||||||
}
|
}
|
||||||
server.ListenAndServe()
|
server.ListenAndServe()
|
||||||
}
|
}
|
||||||
|
51
gemini.go
51
gemini.go
@ -78,12 +78,8 @@ type Request struct {
|
|||||||
// This field is ignored by the server.
|
// This field is ignored by the server.
|
||||||
Host string
|
Host string
|
||||||
|
|
||||||
// TLSConfig provides a TLS configuration for use by the client.
|
// The certificate to use for the request.
|
||||||
// It is recommended that clients set `InsecureSkipVerify` to true to skip
|
Certificate tls.Certificate
|
||||||
// verifying TLS certificates, and instead adopt a Trust-On-First-Use
|
|
||||||
// method of verifying certificates.
|
|
||||||
// This field is ignored by the server.
|
|
||||||
TLSConfig tls.Config
|
|
||||||
|
|
||||||
// RemoteAddr allows servers and other software to record the network
|
// RemoteAddr allows servers and other software to record the network
|
||||||
// address that sent the request.
|
// address that sent the request.
|
||||||
@ -133,7 +129,7 @@ func NewProxyRequest(host, rawurl string) (*Request, error) {
|
|||||||
// write writes the Gemini request to the provided buffered writer.
|
// write writes the Gemini request to the provided buffered writer.
|
||||||
func (r *Request) write(w *bufio.Writer) error {
|
func (r *Request) write(w *bufio.Writer) error {
|
||||||
url := r.URL.String()
|
url := r.URL.String()
|
||||||
// UserInfo is invalid
|
// User is invalid
|
||||||
if r.URL.User != nil || len(url) > 1024 {
|
if r.URL.User != nil || len(url) > 1024 {
|
||||||
return ErrInvalidURL
|
return ErrInvalidURL
|
||||||
}
|
}
|
||||||
@ -165,32 +161,14 @@ type Response struct {
|
|||||||
TLS tls.ConnectionState
|
TLS tls.ConnectionState
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get makes a request for the provided URL. The host is inferred from the URL.
|
|
||||||
//
|
|
||||||
// Get does not verify server certificates. To verify certificates, use a Request.
|
|
||||||
func Get(url string) (*Response, error) {
|
|
||||||
req, err := NewRequest(url)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req.TLSConfig.InsecureSkipVerify = true
|
|
||||||
return Do(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProxyGet requests the provided URL from the provided host.
|
|
||||||
func ProxyGet(host, url string) (*Response, error) {
|
|
||||||
req, err := NewProxyRequest(host, url)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req.TLSConfig.InsecureSkipVerify = true
|
|
||||||
return Do(req)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do sends a Gemini request and returns a Gemini response.
|
// Do sends a Gemini request and returns a Gemini response.
|
||||||
func Do(req *Request) (*Response, error) {
|
func Do(req *Request) (*Response, error) {
|
||||||
// Connect to the host
|
// Connect to the host
|
||||||
conn, err := tls.Dial("tcp", req.Host, &req.TLSConfig)
|
config := &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
Certificates: []tls.Certificate{req.Certificate},
|
||||||
|
}
|
||||||
|
conn, err := tls.Dial("tcp", req.Host, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -267,8 +245,9 @@ type Server struct {
|
|||||||
// If Addr is empty, the server will listen on the address ":1965".
|
// If Addr is empty, the server will listen on the address ":1965".
|
||||||
Addr string
|
Addr string
|
||||||
|
|
||||||
// TLSConfig provides a TLS configuration for use by the server.
|
// Certificate provides a TLS certificate for use by the server.
|
||||||
TLSConfig tls.Config
|
// Using a self-signed certificate is recommended.
|
||||||
|
Certificate tls.Certificate
|
||||||
|
|
||||||
// Handler specifies the Handler for requests.
|
// Handler specifies the Handler for requests.
|
||||||
// If Handler is not set, the server will error.
|
// If Handler is not set, the server will error.
|
||||||
@ -278,6 +257,7 @@ type Server struct {
|
|||||||
// ListenAndServe listens for requests at the server's configured address.
|
// ListenAndServe listens for requests at the server's configured address.
|
||||||
func (s *Server) ListenAndServe() error {
|
func (s *Server) ListenAndServe() error {
|
||||||
addr := s.Addr
|
addr := s.Addr
|
||||||
|
|
||||||
if addr == "" {
|
if addr == "" {
|
||||||
addr = ":1965"
|
addr = ":1965"
|
||||||
}
|
}
|
||||||
@ -288,7 +268,12 @@ func (s *Server) ListenAndServe() error {
|
|||||||
}
|
}
|
||||||
defer ln.Close()
|
defer ln.Close()
|
||||||
|
|
||||||
tlsListener := tls.NewListener(ln, &s.TLSConfig)
|
config := &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
Certificates: []tls.Certificate{s.Certificate},
|
||||||
|
ClientAuth: tls.RequestClientCert,
|
||||||
|
}
|
||||||
|
tlsListener := tls.NewListener(ln, config)
|
||||||
return s.Serve(tlsListener)
|
return s.Serve(tlsListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
70
tofu.go
Normal file
70
tofu.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package gemini
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha512"
|
||||||
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidKnownHosts = errors.New("gemini: invalid known hosts")
|
||||||
|
)
|
||||||
|
|
||||||
|
// KnownHost represents a known host.
|
||||||
|
type KnownHost struct {
|
||||||
|
Hostname string // e.g. gemini.circumlunar.space
|
||||||
|
Algorithm string // fingerprint algorithm
|
||||||
|
Fingerprint string // fingerprint in hexadecimal, with ':' between each octet
|
||||||
|
NotAfter int64 // unix time of certificate notAfter date
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseKnownHosts parses and returns a list of known hosts from the provided io.Reader.
|
||||||
|
func ParseKnownHosts(r io.Reader) ([]KnownHost, error) {
|
||||||
|
hosts := []KnownHost{}
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(r)
|
||||||
|
for scanner.Scan() {
|
||||||
|
text := scanner.Text()
|
||||||
|
|
||||||
|
parts := strings.Split(text, " ")
|
||||||
|
if len(parts) < 4 {
|
||||||
|
return nil, ErrInvalidKnownHosts
|
||||||
|
}
|
||||||
|
|
||||||
|
hostname := parts[0]
|
||||||
|
algorithm := parts[1]
|
||||||
|
fingerprint := parts[2]
|
||||||
|
notAfter, err := strconv.ParseInt(parts[3], 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrInvalidKnownHosts
|
||||||
|
}
|
||||||
|
|
||||||
|
hosts = append(hosts, KnownHost{
|
||||||
|
Hostname: hostname,
|
||||||
|
Algorithm: algorithm,
|
||||||
|
Fingerprint: fingerprint,
|
||||||
|
NotAfter: notAfter,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return hosts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fingerprint returns the SHA-512 fingerprint of the provided certificate.
|
||||||
|
func Fingerprint(cert *x509.Certificate) string {
|
||||||
|
sum512 := sha512.Sum512(cert.Raw)
|
||||||
|
var buf bytes.Buffer
|
||||||
|
for i, f := range sum512 {
|
||||||
|
if i > 0 {
|
||||||
|
fmt.Fprintf(&buf, ":")
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&buf, "%02X", f)
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user