Allow specifying a client certificate (#112)
This commit is contained in:
parent
e4e9c23411
commit
4e26a51741
|
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- Edit current URL with <kbd>e</kbd> (#87)
|
- Edit current URL with <kbd>e</kbd> (#87)
|
||||||
- If `emoji_favicons` is enabled, new bookmarks will have the domain's favicon prepended (#69, #90)
|
- If `emoji_favicons` is enabled, new bookmarks will have the domain's favicon prepended (#69, #90)
|
||||||
- The `BROWSER` env var is now also checked when opening web links on Unix (#93)
|
- The `BROWSER` env var is now also checked when opening web links on Unix (#93)
|
||||||
|
- Allow specifying a client certificate
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Disabling the `color` config setting also disables ANSI colors in pages (#79, #86)
|
- Disabling the `color` config setting also disables ANSI colors in pages (#79, #86)
|
||||||
|
|
|
@ -2,23 +2,74 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/makeworld-the-better-one/go-gemini"
|
"github.com/makeworld-the-better-one/go-gemini"
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var certCache = make(map[string][][]byte)
|
||||||
|
|
||||||
|
func clientCert(host string) ([]byte, []byte) {
|
||||||
|
if cert := certCache[host]; cert != nil {
|
||||||
|
return cert[0], cert[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expand paths staring with ~/
|
||||||
|
certPath, err := homedir.Expand(viper.GetString("auth.certs." + host))
|
||||||
|
if err != nil {
|
||||||
|
certPath = viper.GetString("auth.certs." + host)
|
||||||
|
}
|
||||||
|
keyPath, err := homedir.Expand(viper.GetString("auth.keys." + host))
|
||||||
|
if err != nil {
|
||||||
|
keyPath = viper.GetString("auth.keys." + host)
|
||||||
|
}
|
||||||
|
if certPath == "" && keyPath == "" {
|
||||||
|
certCache[host] = [][]byte{nil, nil}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, err := ioutil.ReadFile(certPath)
|
||||||
|
if err != nil {
|
||||||
|
certCache[host] = [][]byte{nil, nil}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
key, err := ioutil.ReadFile(keyPath)
|
||||||
|
if err != nil {
|
||||||
|
certCache[host] = [][]byte{nil, nil}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
certCache[host] = [][]byte{cert, key}
|
||||||
|
return cert, key
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasClientCert returns whether or not a client certificate exists for a host.
|
||||||
|
func HasClientCert(host string) bool {
|
||||||
|
cert, _ := clientCert(host)
|
||||||
|
return cert != nil
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch returns response data and an error.
|
// Fetch returns response data and an error.
|
||||||
// The error text is human friendly and should be displayed.
|
// The error text is human friendly and should be displayed.
|
||||||
func Fetch(u string) (*gemini.Response, error) {
|
func Fetch(u string) (*gemini.Response, error) {
|
||||||
|
parsed, _ := url.Parse(u)
|
||||||
|
cert, key := clientCert(parsed.Host)
|
||||||
|
|
||||||
res, err := gemini.Fetch(u)
|
var res *gemini.Response
|
||||||
|
var err error
|
||||||
|
if cert != nil {
|
||||||
|
res, err = gemini.FetchWithCert(u, cert, key)
|
||||||
|
} else {
|
||||||
|
res, err = gemini.Fetch(u)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
parsed, _ := url.Parse(u)
|
|
||||||
|
|
||||||
ok := handleTofu(parsed.Hostname(), parsed.Port(), res.Cert)
|
ok := handleTofu(parsed.Hostname(), parsed.Port(), res.Cert)
|
||||||
if !ok {
|
if !ok {
|
||||||
return res, ErrTofu
|
return res, ErrTofu
|
||||||
|
@ -29,7 +80,16 @@ func Fetch(u string) (*gemini.Response, error) {
|
||||||
|
|
||||||
// FetchWithProxy is the same as Fetch, but uses a proxy.
|
// FetchWithProxy is the same as Fetch, but uses a proxy.
|
||||||
func FetchWithProxy(proxyHostname, proxyPort, u string) (*gemini.Response, error) {
|
func FetchWithProxy(proxyHostname, proxyPort, u string) (*gemini.Response, error) {
|
||||||
res, err := gemini.FetchWithHost(net.JoinHostPort(proxyHostname, proxyPort), u)
|
parsed, _ := url.Parse(u)
|
||||||
|
cert, key := clientCert(parsed.Host)
|
||||||
|
|
||||||
|
var res *gemini.Response
|
||||||
|
var err error
|
||||||
|
if cert != nil {
|
||||||
|
res, err = gemini.FetchWithHostAndCert(net.JoinHostPort(proxyHostname, proxyPort), u, cert, key)
|
||||||
|
} else {
|
||||||
|
res, err = gemini.FetchWithHost(net.JoinHostPort(proxyHostname, proxyPort), u)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,18 @@ page_max_time = 10
|
||||||
emoji_favicons = false
|
emoji_favicons = false
|
||||||
|
|
||||||
|
|
||||||
|
[auth]
|
||||||
|
# Authentication settings
|
||||||
|
|
||||||
|
[auth.certs]
|
||||||
|
# Client certificates
|
||||||
|
# "example.com" = "mycert.crt"
|
||||||
|
|
||||||
|
[auth.keys]
|
||||||
|
# Client certificate keys
|
||||||
|
# "example.com" = "mycert.key"
|
||||||
|
|
||||||
|
|
||||||
[keybindings]
|
[keybindings]
|
||||||
# In the future there will be more settings here.
|
# In the future there will be more settings here.
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,18 @@ page_max_time = 10
|
||||||
emoji_favicons = false
|
emoji_favicons = false
|
||||||
|
|
||||||
|
|
||||||
|
[auth]
|
||||||
|
# Authentication settings
|
||||||
|
|
||||||
|
[auth.certs]
|
||||||
|
# Client certificates
|
||||||
|
# "example.com" = "mycert.crt"
|
||||||
|
|
||||||
|
[auth.keys]
|
||||||
|
# Client certificate keys
|
||||||
|
# "example.com" = "mycert.key"
|
||||||
|
|
||||||
|
|
||||||
[keybindings]
|
[keybindings]
|
||||||
# In the future there will be more settings here.
|
# In the future there will be more settings here.
|
||||||
|
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -7,7 +7,7 @@ require (
|
||||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||||
github.com/gdamore/tcell v1.3.1-0.20200608133353-cb1e5d6fa606
|
github.com/gdamore/tcell v1.3.1-0.20200608133353-cb1e5d6fa606
|
||||||
github.com/google/go-cmp v0.5.0 // indirect
|
github.com/google/go-cmp v0.5.0 // indirect
|
||||||
github.com/makeworld-the-better-one/go-gemini v0.8.4
|
github.com/makeworld-the-better-one/go-gemini v0.9.0
|
||||||
github.com/makeworld-the-better-one/go-isemoji v1.1.0
|
github.com/makeworld-the-better-one/go-isemoji v1.1.0
|
||||||
github.com/makeworld-the-better-one/progressbar/v3 v3.3.5-0.20200710151429-125743e22b4f
|
github.com/makeworld-the-better-one/progressbar/v3 v3.3.5-0.20200710151429-125743e22b4f
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -125,8 +125,8 @@ github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tW
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/makeworld-the-better-one/go-gemini v0.8.4 h1:ntsQ9HnlJCmC9PDqXp/f1SCALjBMwh69BbT4BhFRFaw=
|
github.com/makeworld-the-better-one/go-gemini v0.9.0 h1:Iz4ywRDrfsyoR8xZOkSKGXXftMR2spIV6ibVuhrKvSw=
|
||||||
github.com/makeworld-the-better-one/go-gemini v0.8.4/go.mod h1:P7/FbZ+IEIbA/d+A0Y3w2GNgD8SA2AcNv7aDGJbaWG4=
|
github.com/makeworld-the-better-one/go-gemini v0.9.0/go.mod h1:P7/FbZ+IEIbA/d+A0Y3w2GNgD8SA2AcNv7aDGJbaWG4=
|
||||||
github.com/makeworld-the-better-one/go-isemoji v1.1.0 h1:wZBHOKB5zAIgaU2vaWnXFDDhatebB8TySrNVxjVV84g=
|
github.com/makeworld-the-better-one/go-isemoji v1.1.0 h1:wZBHOKB5zAIgaU2vaWnXFDDhatebB8TySrNVxjVV84g=
|
||||||
github.com/makeworld-the-better-one/go-isemoji v1.1.0/go.mod h1:FBjkPl9rr0G4vlZCc+Mr+QcnOfGCTbGWYW8/1sp06I0=
|
github.com/makeworld-the-better-one/go-isemoji v1.1.0/go.mod h1:FBjkPl9rr0G4vlZCc+Mr+QcnOfGCTbGWYW8/1sp06I0=
|
||||||
github.com/makeworld-the-better-one/progressbar/v3 v3.3.5-0.20200710151429-125743e22b4f h1:YEUlTs5gb35UlBLTgqrub9axWTYB3d7/8TxrkJDZpRI=
|
github.com/makeworld-the-better-one/progressbar/v3 v3.3.5-0.20200710151429-125743e22b4f h1:YEUlTs5gb35UlBLTgqrub9axWTYB3d7/8TxrkJDZpRI=
|
||||||
|
|
Loading…
Reference in New Issue