mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-31 20:39:37 +08:00
Update metalinter, fix a few lint warnings and replace deprecated linters. `deadcode`, `structcheck` and `varcheck` are abandoned and now replaced by [`unused`][1]. Since 1.19, `go fmt` reformats godocs according to https://go.dev/doc/comment. I've done a bulk-reformatting of the codebase to keep the linter happy. Backporting is mostly harmless (the exception being `lib/services/role_test.go`, that for some reason breaks the _old_ linter using the new format). [1]: https://golangci-lint.run/usage/linters/ * Bump golangci-lint version * Replace abandoned linters * Fix bodyclose on lib/auth/github.com * Fix bodyclose on lib/kube/proxy/streamproto/proto_test.go * Fix bodyclose on lib/srv/alpnproxy/proxy_test.go * Fix bodyclose on lib/web/conn_upgrade_test.go * Silence staticcheck on lib/kube/proxy/forwarder_test.go * Silence staticcheck on lib/utils/certs_test.go * Address BuildNameToCertificate deprecation warnings * Run `go fmt ./...` * Run `go fmt ./...` on api/ * Ignore formatting in role_test.go * Remove redundant initializers in lib/srv/uacc/ * Update e/
126 lines
3.8 KiB
Go
126 lines
3.8 KiB
Go
/*
|
|
Copyright 2022 Gravitational, Inc.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package web
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/gravitational/trace"
|
|
"github.com/julienschmidt/httprouter"
|
|
|
|
apidefaults "github.com/gravitational/teleport/api/defaults"
|
|
"github.com/gravitational/teleport/api/types"
|
|
"github.com/gravitational/teleport/lib/client/db"
|
|
"github.com/gravitational/teleport/lib/client/identityfile"
|
|
"github.com/gravitational/teleport/lib/httplib"
|
|
"github.com/gravitational/teleport/lib/reversetunnel"
|
|
"github.com/gravitational/teleport/lib/utils"
|
|
)
|
|
|
|
/*
|
|
signDatabaseCertificate returns the necessary files to set up mTLS using the `db` format
|
|
This is the equivalent of running the tctl command
|
|
As an example, requesting:
|
|
POST /webapi/sites/mycluster/sign/db
|
|
|
|
{
|
|
"hostname": "pg.example.com",
|
|
"ttl": "2190h"
|
|
}
|
|
|
|
Should be equivalent to running:
|
|
|
|
tctl auth sign --host=pg.example.com --ttl=2190h --format=db
|
|
|
|
This endpoint returns a tar.gz compressed archive containing the required files to setup mTLS for the database.
|
|
*/
|
|
func (h *Handler) signDatabaseCertificate(w http.ResponseWriter, r *http.Request, p httprouter.Params, site reversetunnel.RemoteSite, token types.ProvisionToken) (interface{}, error) {
|
|
if !token.GetRoles().Include(types.RoleDatabase) {
|
|
return nil, trace.AccessDenied("required '%s' role was not provided by the token", types.RoleDatabase)
|
|
}
|
|
|
|
req := &signDatabaseCertificateReq{}
|
|
if err := httplib.ReadJSON(r, &req); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
if err := req.CheckAndSetDefaults(); err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
virtualFS := identityfile.NewInMemoryConfigWriter()
|
|
|
|
dbCertReq := db.GenerateDatabaseCertificatesRequest{
|
|
ClusterAPI: h.auth.proxyClient,
|
|
Principals: []string{req.Hostname},
|
|
OutputFormat: identityfile.FormatDatabase,
|
|
OutputCanOverwrite: true,
|
|
OutputLocation: "server",
|
|
IdentityFileWriter: virtualFS,
|
|
TTL: req.TTL,
|
|
}
|
|
filesWritten, err := db.GenerateDatabaseCertificates(r.Context(), dbCertReq)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
archiveName := fmt.Sprintf("teleport_mTLS_%s.tar.gz", req.Hostname)
|
|
archiveBytes, err := utils.CompressTarGzArchive(filesWritten, virtualFS)
|
|
if err != nil {
|
|
return nil, trace.Wrap(err)
|
|
}
|
|
|
|
// Set file name
|
|
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment;filename="%v"`, archiveName))
|
|
|
|
// ServeContent sets the correct headers: Content-Type, Content-Length and Accept-Ranges.
|
|
// It also handles the Range negotiation
|
|
http.ServeContent(w, r, archiveName, time.Now(), bytes.NewReader(archiveBytes.Bytes()))
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
type signDatabaseCertificateReq struct {
|
|
Hostname string `json:"hostname,omitempty"`
|
|
TTLRaw string `json:"ttl,omitempty"`
|
|
|
|
TTL time.Duration `json:"-"`
|
|
}
|
|
|
|
// CheckAndSetDefaults will validate and convert the received values
|
|
// Hostname must not be empty
|
|
// TTL must either be a valid time.Duration or empty (inherits apidefaults.CertDuration)
|
|
func (s *signDatabaseCertificateReq) CheckAndSetDefaults() error {
|
|
if s.Hostname == "" {
|
|
return trace.BadParameter("missing hostname")
|
|
}
|
|
|
|
if s.TTLRaw == "" {
|
|
s.TTLRaw = apidefaults.CertDuration.String()
|
|
}
|
|
ttl, err := time.ParseDuration(s.TTLRaw)
|
|
if err != nil {
|
|
return trace.BadParameter("invalid ttl '%s', use https://pkg.go.dev/time#ParseDuration format (example: 2190h)", s.TTLRaw)
|
|
}
|
|
s.TTL = ttl
|
|
|
|
return nil
|
|
}
|