Added openapi description
This commit is contained in:
parent
37a1a98c49
commit
251beb24d3
2
app.go
2
app.go
|
@ -173,7 +173,7 @@ func run(args options) {
|
||||||
for { // this is an endless loop
|
for { // this is an endless loop
|
||||||
httpsServer.cond.L.Lock()
|
httpsServer.cond.L.Lock()
|
||||||
// this mechanism doesn't let us through until all conditions are ment
|
// this mechanism doesn't let us through until all conditions are ment
|
||||||
for config.TLS.Enabled == false || config.TLS.PortHTTPS == 0 || config.TLS.PrivateKey == "" || config.TLS.CertificateChain == "" { // sleep until neccessary data is supplied
|
for config.TLS.Enabled == false || config.TLS.PortHTTPS == 0 || config.TLS.PrivateKey == "" || config.TLS.CertificateChain == "" { // sleep until necessary data is supplied
|
||||||
httpsServer.cond.Wait()
|
httpsServer.cond.Wait()
|
||||||
}
|
}
|
||||||
address := net.JoinHostPort(config.BindHost, strconv.Itoa(config.TLS.PortHTTPS))
|
address := net.JoinHostPort(config.BindHost, strconv.Itoa(config.TLS.PortHTTPS))
|
||||||
|
|
33
config.go
33
config.go
|
@ -63,37 +63,34 @@ type dnsConfig struct {
|
||||||
var defaultDNS = []string{"tls://1.1.1.1", "tls://1.0.0.1"}
|
var defaultDNS = []string{"tls://1.1.1.1", "tls://1.0.0.1"}
|
||||||
|
|
||||||
type tlsConfigSettings struct {
|
type tlsConfigSettings struct {
|
||||||
Enabled bool `yaml:"enabled" json:"enabled"`
|
Enabled bool `yaml:"enabled" json:"enabled"` // Enabled is the encryption (DOT/DOH/HTTPS) status
|
||||||
ServerName string `yaml:"server_name" json:"server_name,omitempty"`
|
ServerName string `yaml:"server_name" json:"server_name,omitempty"` // ServerName is the hostname of your HTTPS/TLS server
|
||||||
ForceHTTPS bool `yaml:"force_https" json:"force_https,omitempty"`
|
ForceHTTPS bool `yaml:"force_https" json:"force_https,omitempty"` // ForceHTTPS: if true, forces HTTP->HTTPS redirect
|
||||||
PortHTTPS int `yaml:"port_https" json:"port_https,omitempty"`
|
PortHTTPS int `yaml:"port_https" json:"port_https,omitempty"` // HTTPS port. If 0, HTTPS will be disabled
|
||||||
PortDNSOverTLS int `yaml:"port_dns_over_tls" json:"port_dns_over_tls,omitempty"`
|
PortDNSOverTLS int `yaml:"port_dns_over_tls" json:"port_dns_over_tls,omitempty"` // DNS-over-TLS port. If 0, DOT will be disabled
|
||||||
|
|
||||||
dnsforward.TLSConfig `yaml:",inline" json:",inline"`
|
dnsforward.TLSConfig `yaml:",inline" json:",inline"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// field ordering is not important -- these are for API and are recalculated on each run
|
// field ordering is not important -- these are for API and are recalculated on each run
|
||||||
type tlsConfigStatus struct {
|
type tlsConfigStatus struct {
|
||||||
// certificate status
|
ValidCert bool `yaml:"-" json:"valid_cert"` // ValidCert is true if the specified certificates chain is a valid chain of X509 certificates
|
||||||
ValidCert bool `yaml:"-" json:"valid_cert"`
|
ValidChain bool `yaml:"-" json:"valid_chain"` // ValidChain is true if the specified certificates chain is verified and issued by a known CA
|
||||||
ValidChain bool `yaml:"-" json:"valid_chain"`
|
Subject string `yaml:"-" json:"subject,omitempty"` // Subject is the subject of the first certificate in the chain
|
||||||
Subject string `yaml:"-" json:"subject,omitempty"`
|
Issuer string `yaml:"-" json:"issuer,omitempty"` // Issuer is the issuer of the first certificate in the chain
|
||||||
Issuer string `yaml:"-" json:"issuer,omitempty"`
|
NotBefore time.Time `yaml:"-" json:"not_before,omitempty"` // NotBefore is the NotBefore field of the first certificate in the chain
|
||||||
NotBefore time.Time `yaml:"-" json:"not_before,omitempty"`
|
NotAfter time.Time `yaml:"-" json:"not_after,omitempty"` // NotAfter is the NotAfter field of the first certificate in the chain
|
||||||
NotAfter time.Time `yaml:"-" json:"not_after,omitempty"`
|
DNSNames []string `yaml:"-" json:"dns_names"` // DNSNames is the value of SubjectAltNames field of the first certificate in the chain
|
||||||
DNSNames []string `yaml:"-" json:"dns_names"`
|
|
||||||
StatusCertificate string `yaml:"-" json:"status_cert,omitempty"`
|
|
||||||
|
|
||||||
// key status
|
// key status
|
||||||
ValidKey bool `yaml:"-" json:"valid_key"`
|
ValidKey bool `yaml:"-" json:"valid_key"` // ValidKey is true if the key is a valid private key
|
||||||
KeyType string `yaml:"-" json:"key_type,omitempty"`
|
KeyType string `yaml:"-" json:"key_type,omitempty"` // KeyType is one of RSA or ECDSA
|
||||||
|
|
||||||
// is usable? set by validator
|
// is usable? set by validator
|
||||||
usable bool
|
usable bool
|
||||||
|
|
||||||
// warnings
|
// warnings
|
||||||
Warning string `yaml:"-" json:"warning,omitempty"`
|
WarningValidation string `yaml:"-" json:"warning_validation,omitempty"` // WarningValidation is a validation warning message with the issue description
|
||||||
WarningValidation string `yaml:"-" json:"warning_validation,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// field ordering is important -- yaml fields will mirror ordering from here
|
// field ordering is important -- yaml fields will mirror ordering from here
|
||||||
|
|
24
control.go
24
control.go
|
@ -1118,7 +1118,7 @@ func validateCertificates(data tlsConfig) tlsConfig {
|
||||||
// clear out status for certificates
|
// clear out status for certificates
|
||||||
data.tlsConfigStatus = tlsConfigStatus{}
|
data.tlsConfigStatus = tlsConfigStatus{}
|
||||||
|
|
||||||
// check only public certificate separetely from the key
|
// check only public certificate separately from the key
|
||||||
if data.CertificateChain != "" {
|
if data.CertificateChain != "" {
|
||||||
log.Tracef("got certificate: %s", data.CertificateChain)
|
log.Tracef("got certificate: %s", data.CertificateChain)
|
||||||
|
|
||||||
|
@ -1194,24 +1194,6 @@ func validateCertificates(data tlsConfig) tlsConfig {
|
||||||
data.NotAfter = notAfter
|
data.NotAfter = notAfter
|
||||||
data.NotBefore = mainCert.NotBefore
|
data.NotBefore = mainCert.NotBefore
|
||||||
data.DNSNames = mainCert.DNSNames
|
data.DNSNames = mainCert.DNSNames
|
||||||
|
|
||||||
data.StatusCertificate = fmt.Sprintf("Certificate expires on %s", notAfter) //, valid for hostname %s", mainCert.NotAfter, mainCert.Subject.CommonName)
|
|
||||||
if len(mainCert.DNSNames) == 1 {
|
|
||||||
data.StatusCertificate += fmt.Sprintf(", valid for hostname %s", mainCert.DNSNames[0])
|
|
||||||
} else if len(mainCert.DNSNames) > 1 {
|
|
||||||
data.StatusCertificate += ", valid for hostnames " + strings.Join(mainCert.DNSNames, ", ")
|
|
||||||
}
|
|
||||||
|
|
||||||
// issue a warning if certificate is about to expire
|
|
||||||
now := time.Now()
|
|
||||||
if now.AddDate(0, 0, 30).After(notAfter) {
|
|
||||||
timeLeft := notAfter.Sub(now)
|
|
||||||
if timeLeft > 0 {
|
|
||||||
data.Warning = fmt.Sprintf("Your certificate expires in %.0f days, we recommend you update it soon", timeLeft.Hours()/24)
|
|
||||||
} else {
|
|
||||||
data.Warning = fmt.Sprintf("Your certificate has expired on %s, we recommend you update it immediatedly", mainCert.NotAfter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1322,11 +1304,11 @@ func marshalTLS(w http.ResponseWriter, data tlsConfig) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
if data.CertificateChain != "" {
|
if data.CertificateChain != "" {
|
||||||
encoded := base64.StdEncoding.EncodeToString([]byte(data.CertificateChain))
|
encoded := base64.StdEncoding.EncodeToString([]byte(data.CertificateChain))
|
||||||
data.CertificateChain = string(encoded)
|
data.CertificateChain = encoded
|
||||||
}
|
}
|
||||||
if data.PrivateKey != "" {
|
if data.PrivateKey != "" {
|
||||||
encoded := base64.StdEncoding.EncodeToString([]byte(data.PrivateKey))
|
encoded := base64.StdEncoding.EncodeToString([]byte(data.PrivateKey))
|
||||||
data.PrivateKey = string(encoded)
|
data.PrivateKey = encoded
|
||||||
}
|
}
|
||||||
err := json.NewEncoder(w).Encode(data)
|
err := json.NewEncoder(w).Encode(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
29
helpers.go
29
helpers.go
|
@ -6,7 +6,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/rand"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -257,34 +256,6 @@ func checkPacketPortAvailable(host string, port int) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------
|
|
||||||
// random string generation
|
|
||||||
// ------------------------
|
|
||||||
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
||||||
const (
|
|
||||||
letterIdxBits = 6 // 6 bits to represent a letter index
|
|
||||||
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
|
|
||||||
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
|
|
||||||
)
|
|
||||||
|
|
||||||
func RandStringBytesMaskImpr(n int) string {
|
|
||||||
b := make([]byte, n)
|
|
||||||
// A rand.Int63() generates 63 random bits, enough for letterIdxMax letters!
|
|
||||||
for i, cache, remain := n-1, rand.Int63(), letterIdxMax; i >= 0; {
|
|
||||||
if remain == 0 {
|
|
||||||
cache, remain = rand.Int63(), letterIdxMax
|
|
||||||
}
|
|
||||||
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
|
|
||||||
b[i] = letterBytes[idx]
|
|
||||||
i--
|
|
||||||
}
|
|
||||||
cache >>= letterIdxBits
|
|
||||||
remain--
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------
|
// ---------------------
|
||||||
// debug logging helpers
|
// debug logging helpers
|
||||||
// ---------------------
|
// ---------------------
|
||||||
|
|
|
@ -2,7 +2,7 @@ swagger: '2.0'
|
||||||
info:
|
info:
|
||||||
title: 'AdGuard Home'
|
title: 'AdGuard Home'
|
||||||
description: 'AdGuard Home REST API. Admin web interface is built on top of this REST API.'
|
description: 'AdGuard Home REST API. Admin web interface is built on top of this REST API.'
|
||||||
version: 0.92.0
|
version: 0.93.0
|
||||||
schemes:
|
schemes:
|
||||||
- http
|
- http
|
||||||
basePath: /control
|
basePath: /control
|
||||||
|
@ -12,6 +12,9 @@ tags:
|
||||||
-
|
-
|
||||||
name: global
|
name: global
|
||||||
description: 'AdGuard Home server general settings and controls'
|
description: 'AdGuard Home server general settings and controls'
|
||||||
|
-
|
||||||
|
name: tls
|
||||||
|
description: 'AdGuard Home HTTPS/DOH/DOT settings'
|
||||||
-
|
-
|
||||||
name: log
|
name: log
|
||||||
description: 'AdGuard Home query log'
|
description: 'AdGuard Home query log'
|
||||||
|
@ -267,6 +270,70 @@ paths:
|
||||||
200:
|
200:
|
||||||
description: OK
|
description: OK
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
|
# TLS server methods
|
||||||
|
# --------------------------------------------------
|
||||||
|
|
||||||
|
/tls/status:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- tls
|
||||||
|
operationId: tlsStatus
|
||||||
|
summary: "Returns TLS configuration and its status"
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/TlsConfig"
|
||||||
|
|
||||||
|
/tls/configure:
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- tls
|
||||||
|
operationId: tlsConfigure
|
||||||
|
summary: "Updates current TLS configuration"
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: "body"
|
||||||
|
name: "body"
|
||||||
|
description: "TLS configuration JSON"
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/TlsConfig"
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: "TLS configuration and its status"
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/TlsConfig"
|
||||||
|
400:
|
||||||
|
description: "Invalid configuration or unavailable port"
|
||||||
|
500:
|
||||||
|
description: "Error occurred while applying configuration"
|
||||||
|
|
||||||
|
/tls/validate:
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- tls
|
||||||
|
operationId: tlsValidate
|
||||||
|
summary: "Checks if the current TLS configuration is valid"
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: "body"
|
||||||
|
name: "body"
|
||||||
|
description: "TLS configuration JSON"
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/TlsConfig"
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: "TLS configuration and its status"
|
||||||
|
schema:
|
||||||
|
$ref: "#/definitions/TlsConfig"
|
||||||
|
400:
|
||||||
|
description: "Invalid configuration or unavailable port"
|
||||||
|
|
||||||
# --------------------------------------------------
|
# --------------------------------------------------
|
||||||
# DHCP server methods
|
# DHCP server methods
|
||||||
# --------------------------------------------------
|
# --------------------------------------------------
|
||||||
|
@ -1064,3 +1131,80 @@ definitions:
|
||||||
description: "Query log"
|
description: "Query log"
|
||||||
items:
|
items:
|
||||||
$ref: "#/definitions/QueryLogItem"
|
$ref: "#/definitions/QueryLogItem"
|
||||||
|
TlsConfig:
|
||||||
|
type: "object"
|
||||||
|
description: "TLS configuration settings and status"
|
||||||
|
properties:
|
||||||
|
# TLS configuration
|
||||||
|
enabled:
|
||||||
|
type: "boolean"
|
||||||
|
example: "true"
|
||||||
|
description: "enabled is the encryption (DOT/DOH/HTTPS) status"
|
||||||
|
server_name:
|
||||||
|
type: "string"
|
||||||
|
example: "example.org"
|
||||||
|
description: "server_name is the hostname of your HTTPS/TLS server"
|
||||||
|
force_https:
|
||||||
|
type: "boolean"
|
||||||
|
example: "true"
|
||||||
|
description: "if true, forces HTTP->HTTPS redirect"
|
||||||
|
port_https:
|
||||||
|
type: "integer"
|
||||||
|
format: "int32"
|
||||||
|
example: 443
|
||||||
|
description: "HTTPS port. If 0, HTTPS will be disabled."
|
||||||
|
port_dns_over_tls:
|
||||||
|
type: "integer"
|
||||||
|
format: "int32"
|
||||||
|
example: 853
|
||||||
|
description: "DNS-over-TLS port. If 0, DOT will be disabled."
|
||||||
|
certificate_chain:
|
||||||
|
type: "string"
|
||||||
|
description: "Base64 string with PEM-encoded certificates chain"
|
||||||
|
private_key:
|
||||||
|
type: "string"
|
||||||
|
description: "Base64 string with PEM-encoded private key"
|
||||||
|
# Below goes validation fields
|
||||||
|
valid_cert:
|
||||||
|
type: "boolean"
|
||||||
|
example: "true"
|
||||||
|
description: "valid_cert is true if the specified certificates chain is a valid chain of X509 certificates"
|
||||||
|
valid_chain:
|
||||||
|
type: "boolean"
|
||||||
|
example: "true"
|
||||||
|
description: "valid_chain is true if the specified certificates chain is verified and issued by a known CA"
|
||||||
|
subject:
|
||||||
|
type: "string"
|
||||||
|
example: "CN=example.org"
|
||||||
|
description: "subject is the subject of the first certificate in the chain"
|
||||||
|
issuer:
|
||||||
|
type: "string"
|
||||||
|
example: "CN=Let's Encrypt Authority X3,O=Let's Encrypt,C=US"
|
||||||
|
description: "issuer is the issuer of the first certificate in the chain"
|
||||||
|
not_before:
|
||||||
|
type: "string"
|
||||||
|
example: "2019-01-31T10:47:32Z"
|
||||||
|
description: "not_before is the NotBefore field of the first certificate in the chain"
|
||||||
|
not_after:
|
||||||
|
type: "string"
|
||||||
|
example: "2019-05-01T10:47:32Z"
|
||||||
|
description: "not_after is the NotAfter field of the first certificate in the chain"
|
||||||
|
dns_names:
|
||||||
|
type: "array"
|
||||||
|
items:
|
||||||
|
type: "string"
|
||||||
|
description: "dns_names is the value of SubjectAltNames field of the first certificate in the chain"
|
||||||
|
example:
|
||||||
|
- "*.example.org"
|
||||||
|
valid_key:
|
||||||
|
type: "boolean"
|
||||||
|
example: "true"
|
||||||
|
description: "valid_key is true if the key is a valid private key"
|
||||||
|
key_type:
|
||||||
|
type: "string"
|
||||||
|
example: "RSA"
|
||||||
|
description: "key_type is either RSA or ECDSA"
|
||||||
|
warning_validation:
|
||||||
|
type: "string"
|
||||||
|
example: "You have specified an empty certificate"
|
||||||
|
description: "warning_validation is a validation warning message with the issue description"
|
Loading…
Reference in New Issue