Unverified Commit 6d3c31ad authored by Hussein Galal's avatar Hussein Galal Committed by GitHub

[Release-1.20] Add cert rotation command (#4633)

* Add cert rotation command (#4495) * Add cert rotation command Signed-off-by: 's avatargalal-hussein <hussein.galal.ahmed.11@gmail.com> * add function to check for dynamic listener file Signed-off-by: 's avatarBrian Downs <brian.downs@gmail.com> * Add dynamiclistener cert rotation support Signed-off-by: 's avatargalal-hussein <hussein.galal.ahmed.11@gmail.com> * fixes to the cert rotation Signed-off-by: 's avatargalal-hussein <hussein.galal.ahmed.11@gmail.com> * fix ci tests Signed-off-by: 's avatargalal-hussein <hussein.galal.ahmed.11@gmail.com> * fixes to certificate rotation command Signed-off-by: 's avatargalal-hussein <hussein.galal.ahmed.11@gmail.com> * more fixes Signed-off-by: 's avatargalal-hussein <hussein.galal.ahmed.11@gmail.com> Co-authored-by: 's avatarBrian Downs <brian.downs@gmail.com> * Upgrade dynamic listener Signed-off-by: 's avatargalal-hussein <hussein.galal.ahmed.11@gmail.com> * go mod tidy Signed-off-by: 's avatargalal-hussein <hussein.galal.ahmed.11@gmail.com> Co-authored-by: 's avatarBrian Downs <brian.downs@gmail.com>
parent be62f439
package main
import (
"context"
"errors"
"os"
"github.com/rancher/k3s/pkg/cli/cert"
"github.com/rancher/k3s/pkg/cli/cmds"
"github.com/rancher/k3s/pkg/configfilearg"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
func main() {
app := cmds.NewApp()
app.Commands = []cli.Command{
cmds.NewCertCommand(
cmds.NewCertSubcommands(
cert.Run),
),
}
if err := app.Run(configfilearg.MustParse(os.Args)); err != nil && !errors.Is(err, context.Canceled) {
logrus.Fatal(err)
}
}
......@@ -34,6 +34,7 @@ func main() {
}
etcdsnapshotCommand := internalCLIAction(version.Program+"-"+cmds.EtcdSnapshotCommand, dataDir, os.Args)
certCommand := internalCLIAction(version.Program+"-"+cmds.CertCommand, dataDir, os.Args)
// Handle subcommand invocation (k3s server, k3s crictl, etc)
app := cmds.NewApp()
......@@ -51,6 +52,10 @@ func main() {
etcdsnapshotCommand,
etcdsnapshotCommand),
),
cmds.NewCertCommand(
cmds.NewCertSubcommands(
certCommand),
),
}
if err := app.Run(os.Args); err != nil {
......
......@@ -7,6 +7,7 @@ import (
"github.com/docker/docker/pkg/reexec"
crictl2 "github.com/kubernetes-sigs/cri-tools/cmd/crictl"
"github.com/rancher/k3s/pkg/cli/agent"
"github.com/rancher/k3s/pkg/cli/cert"
"github.com/rancher/k3s/pkg/cli/cmds"
"github.com/rancher/k3s/pkg/cli/crictl"
"github.com/rancher/k3s/pkg/cli/ctr"
......@@ -50,6 +51,10 @@ func main() {
etcdsnapshot.Prune,
etcdsnapshot.Run),
),
cmds.NewCertCommand(
cmds.NewCertSubcommands(
cert.Run),
),
}
err := app.Run(configfilearg.MustParse(os.Args))
......
......@@ -101,9 +101,10 @@ require (
// LOOK TO scripts/download FOR THE VERSION OF runc THAT WE ARE BUILDING/SHIPPING
github.com/opencontainers/runc v1.0.0-rc94
github.com/opencontainers/selinux v1.8.0
github.com/otiai10/copy v1.6.0
github.com/pierrec/lz4 v2.5.2+incompatible
github.com/pkg/errors v0.9.1
github.com/rancher/dynamiclistener v0.2.2
github.com/rancher/dynamiclistener v0.2.3-k3s1
github.com/rancher/remotedialer v0.2.0
github.com/rancher/wrangler v0.6.2
github.com/rancher/wrangler-api v0.6.0
......
......@@ -745,6 +745,14 @@ github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mo
github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE=
github.com/opencontainers/selinux v1.8.0 h1:+77ba4ar4jsCbL1GLbFL8fFM57w6suPfSS9PDLDY7KM=
github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
github.com/otiai10/copy v1.6.0 h1:IinKAryFFuPONZ7cm6T6E2QX/vcJwSnlaA5lfoaXIiQ=
github.com/otiai10/copy v1.6.0/go.mod h1:XWfuS3CrI0R6IE0FbgHsEazaXO8G0LpMp9o8tos0x4E=
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
github.com/otiai10/curr v1.0.0 h1:TJIWdbX0B+kpNagQrjgq8bCMrbhiuX73M2XwgtDMoOI=
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
github.com/otiai10/mint v1.3.2 h1:VYWnrP5fXmz1MXvjuUvcBrXSjGE6xjON+axB/UrpO3E=
github.com/otiai10/mint v1.3.2/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
......@@ -790,8 +798,8 @@ github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULU
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/quobyte/api v0.1.8/go.mod h1:jL7lIHrmqQ7yh05OJ+eEEdHr0u/kmT1Ff9iHd+4H6VI=
github.com/rancher/dynamiclistener v0.2.2 h1:70dMwOr1sqb6mQqfU2nDb/fr5cv7HJjH+kFYzoxb8KU=
github.com/rancher/dynamiclistener v0.2.2/go.mod h1:9WusTANoiRr8cDWCTtf5txieulezHbpv4vhLADPp0zU=
github.com/rancher/dynamiclistener v0.2.3-k3s1 h1:BC4EQc2vJHRGiSbPYuA2DsjJeikgBMTDpR36932jAc0=
github.com/rancher/dynamiclistener v0.2.3-k3s1/go.mod h1:9WusTANoiRr8cDWCTtf5txieulezHbpv4vhLADPp0zU=
github.com/rancher/moq v0.0.0-20190404221404-ee5226d43009/go.mod h1:wpITyDPTi/Na5h73XkbuEf2AP9fbgrIGqqxVzFhYD6U=
github.com/rancher/remotedialer v0.2.0 h1:xD7t3K6JYwTdAsxmGtTHQMkEkFgKouQ1foLxVW424Dc=
github.com/rancher/remotedialer v0.2.0/go.mod h1:tkU8ZvrR5lRgaKWaX71nAy6daeqvPFx/lJEnbW7tXSI=
......
......@@ -10,6 +10,7 @@ import (
"os"
"github.com/rancher/k3s/pkg/cli/agent"
"github.com/rancher/k3s/pkg/cli/cert"
"github.com/rancher/k3s/pkg/cli/cmds"
"github.com/rancher/k3s/pkg/cli/crictl"
"github.com/rancher/k3s/pkg/cli/etcdsnapshot"
......@@ -34,6 +35,10 @@ func main() {
etcdsnapshot.Prune,
etcdsnapshot.Run),
),
cmds.NewCertCommand(
cmds.NewCertSubcommands(
cert.Run),
),
}
if err := app.Run(configfilearg.MustParse(os.Args)); err != nil {
......
package cmds
import (
"github.com/rancher/k3s/pkg/version"
"github.com/urfave/cli"
)
const CertCommand = "certificate"
var (
ServicesList cli.StringSlice
CertCommandFlags = []cli.Flag{
DebugFlag,
ConfigFlag,
LogFile,
AlsoLogToStderr,
cli.StringFlag{
Name: "data-dir,d",
Usage: "(data) Folder to hold state default /var/lib/rancher/" + version.Program + " or ${HOME}/.rancher/" + version.Program + " if not root",
Destination: &ServerConfig.DataDir,
},
cli.StringSliceFlag{
Name: "service,s",
Usage: "List of services to rotate certificates for. Options include (admin, api-server, controller-manager, scheduler, " + version.Program + "-controller, " + version.Program + "-server, cloud-controller, etcd, auth-proxy, kubelet, kube-proxy)",
Value: &ServicesList,
},
}
)
func NewCertCommand(subcommands []cli.Command) cli.Command {
return cli.Command{
Name: CertCommand,
Usage: "Certificates management",
SkipFlagParsing: false,
SkipArgReorder: true,
Subcommands: subcommands,
Flags: CertCommandFlags,
}
}
func NewCertSubcommands(rotate func(ctx *cli.Context) error) []cli.Command {
return []cli.Command{
{
Name: "rotate",
Usage: "Certificate rotation",
SkipFlagParsing: false,
SkipArgReorder: true,
Action: rotate,
Flags: CertCommandFlags,
},
}
}
......@@ -52,6 +52,15 @@ func (c *Cluster) newListener(ctx context.Context) (net.Listener, http.Handler,
MinVersion: c.config.TLSMinVersion,
CipherSuites: c.config.TLSCipherSuites,
},
RegenerateCerts: func() bool {
const regenerateDynamicListenerFile = "dynamic-cert-regenerate"
dynamicListenerRegenFilePath := filepath.Join(c.config.DataDir, "tls", regenerateDynamicListenerFile)
if _, err := os.Stat(dynamicListenerRegenFilePath); err == nil {
os.Remove(dynamicListenerRegenFilePath)
return true
}
return false
},
})
}
......
......@@ -78,6 +78,7 @@ rm -f \
bin/containerd-shim-runc-v2 \
bin/k3s-server \
bin/k3s-etcd-snapshot \
bin/k3s-certificate \
bin/kubectl \
bin/crictl \
bin/ctr
......@@ -107,6 +108,7 @@ CGO_ENABLED=1 "${GO}" build -tags "$TAGS" -ldflags "$VERSIONFLAGS $LDFLAGS $STAT
ln -s containerd ./bin/k3s-agent
ln -s containerd ./bin/k3s-server
ln -s containerd ./bin/k3s-etcd-snapshot
ln -s containerd ./bin/k3s-certificate
ln -s containerd ./bin/kubectl
ln -s containerd ./bin/crictl
ln -s containerd ./bin/ctr
......
......@@ -7,7 +7,7 @@ cd $(dirname $0)/..
GO=${GO-go}
for i in crictl kubectl k3s-agent k3s-server k3s-etcd-snapshot k3s; do
for i in crictl kubectl k3s-agent k3s-server k3s-etcd-snapshot k3s-certificate k3s ; do
rm -f bin/$i
ln -s containerd bin/$i
done
......
test/data.copy
coverage.txt
vendor
.vagrant
.idea/
The MIT License (MIT)
Copyright (c) 2018 otiai10
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
# copy
[![Go Reference](https://pkg.go.dev/badge/github.com/otiai10/copy.svg)](https://pkg.go.dev/github.com/otiai10/copy)
[![Actions Status](https://github.com/otiai10/copy/workflows/Go/badge.svg)](https://github.com/otiai10/copy/actions)
[![codecov](https://codecov.io/gh/otiai10/copy/branch/main/graph/badge.svg)](https://codecov.io/gh/otiai10/copy)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/otiai10/copy/blob/main/LICENSE)
[![Go Report Card](https://goreportcard.com/badge/github.com/otiai10/copy)](https://goreportcard.com/report/github.com/otiai10/copy)
[![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/otiai10/copy?sort=semver)](https://pkg.go.dev/github.com/otiai10/copy)
`copy` copies directories recursively.
# Example Usage
```go
err := Copy("your/directory", "your/directory.copy")
```
# Advanced Usage
```go
// Options specifies optional actions on copying.
type Options struct {
// OnSymlink can specify what to do on symlink
OnSymlink func(src string) SymlinkAction
// OnDirExists can specify what to do when there is a directory already existing in destination.
OnDirExists func(src, dest string) DirExistsAction
// Skip can specify which files should be skipped
Skip func(src string) (bool, error)
// AddPermission to every entities,
// NO MORE THAN 0777
AddPermission os.FileMode
// Sync file after copy.
// Useful in case when file must be on the disk
// (in case crash happens, for example),
// at the expense of some performance penalty
Sync bool
// Preserve the atime and the mtime of the entries
// On linux we can preserve only up to 1 millisecond accuracy
PreserveTimes bool
}
```
```go
// For example...
opt := Options{
Skip: func(src string) (bool, error) {
return strings.HasSuffix(src, ".git"), nil
},
}
err := Copy("your/directory", "your/directory.copy", opt)
```
# Issues
- https://github.com/otiai10/copy/issues
package copy
import (
"io"
"io/ioutil"
"os"
"path/filepath"
"time"
)
const (
// tmpPermissionForDirectory makes the destination directory writable,
// so that stuff can be copied recursively even if any original directory is NOT writable.
// See https://github.com/otiai10/copy/pull/9 for more information.
tmpPermissionForDirectory = os.FileMode(0755)
)
type timespec struct {
Mtime time.Time
Atime time.Time
Ctime time.Time
}
// Copy copies src to dest, doesn't matter if src is a directory or a file.
func Copy(src, dest string, opt ...Options) error {
info, err := os.Lstat(src)
if err != nil {
return err
}
return switchboard(src, dest, info, assure(src, dest, opt...))
}
// switchboard switches proper copy functions regarding file type, etc...
// If there would be anything else here, add a case to this switchboard.
func switchboard(src, dest string, info os.FileInfo, opt Options) (err error) {
switch {
case info.Mode()&os.ModeSymlink != 0:
err = onsymlink(src, dest, info, opt)
case info.IsDir():
err = dcopy(src, dest, info, opt)
case info.Mode()&os.ModeNamedPipe != 0:
err = pcopy(dest, info)
default:
err = fcopy(src, dest, info, opt)
}
return err
}
// copyNextOrSkip decide if this src should be copied or not.
// Because this "copy" could be called recursively,
// "info" MUST be given here, NOT nil.
func copyNextOrSkip(src, dest string, info os.FileInfo, opt Options) error {
skip, err := opt.Skip(src)
if err != nil {
return err
}
if skip {
return nil
}
return switchboard(src, dest, info, opt)
}
// fcopy is for just a file,
// with considering existence of parent directory
// and file permission.
func fcopy(src, dest string, info os.FileInfo, opt Options) (err error) {
if err = os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil {
return
}
f, err := os.Create(dest)
if err != nil {
return
}
defer fclose(f, &err)
if err = os.Chmod(f.Name(), info.Mode()|opt.AddPermission); err != nil {
return
}
s, err := os.Open(src)
if err != nil {
return
}
defer fclose(s, &err)
var buf []byte = nil
var w io.Writer = f
// var r io.Reader = s
if opt.CopyBufferSize != 0 {
buf = make([]byte, opt.CopyBufferSize)
// Disable using `ReadFrom` by io.CopyBuffer.
// See https://github.com/otiai10/copy/pull/60#discussion_r627320811 for more details.
w = struct{ io.Writer }{f}
// r = struct{ io.Reader }{s}
}
if _, err = io.CopyBuffer(w, s, buf); err != nil {
return err
}
if opt.Sync {
err = f.Sync()
}
if opt.PreserveTimes {
return preserveTimes(info, dest)
}
return
}
// dcopy is for a directory,
// with scanning contents inside the directory
// and pass everything to "copy" recursively.
func dcopy(srcdir, destdir string, info os.FileInfo, opt Options) (err error) {
_, err = os.Stat(destdir)
if err == nil && opt.OnDirExists != nil && destdir != opt.intent.dest {
switch opt.OnDirExists(srcdir, destdir) {
case Replace:
if err := os.RemoveAll(destdir); err != nil {
return err
}
case Untouchable:
return nil
} // case "Merge" is default behaviour. Go through.
} else if err != nil && !os.IsNotExist(err) {
return err // Unwelcome error type...!
}
originalMode := info.Mode()
// Make dest dir with 0755 so that everything writable.
if err = os.MkdirAll(destdir, tmpPermissionForDirectory); err != nil {
return
}
// Recover dir mode with original one.
defer chmod(destdir, originalMode|opt.AddPermission, &err)
contents, err := ioutil.ReadDir(srcdir)
if err != nil {
return
}
for _, content := range contents {
cs, cd := filepath.Join(srcdir, content.Name()), filepath.Join(destdir, content.Name())
if err = copyNextOrSkip(cs, cd, content, opt); err != nil {
// If any error, exit immediately
return
}
}
if opt.PreserveTimes {
return preserveTimes(info, destdir)
}
return
}
func onsymlink(src, dest string, info os.FileInfo, opt Options) error {
switch opt.OnSymlink(src) {
case Shallow:
return lcopy(src, dest)
case Deep:
orig, err := os.Readlink(src)
if err != nil {
return err
}
info, err = os.Lstat(orig)
if err != nil {
return err
}
return copyNextOrSkip(orig, dest, info, opt)
case Skip:
fallthrough
default:
return nil // do nothing
}
}
// lcopy is for a symlink,
// with just creating a new symlink by replicating src symlink.
func lcopy(src, dest string) error {
src, err := os.Readlink(src)
if err != nil {
return err
}
return os.Symlink(src, dest)
}
// fclose ANYHOW closes file,
// with asiging error raised during Close,
// BUT respecting the error already reported.
func fclose(f *os.File, reported *error) {
if err := f.Close(); *reported == nil {
*reported = err
}
}
// chmod ANYHOW changes file mode,
// with asiging error raised during Chmod,
// BUT respecting the error already reported.
func chmod(dir string, mode os.FileMode, reported *error) {
if err := os.Chmod(dir, mode); *reported == nil {
*reported = err
}
}
// assure Options struct, should be called only once.
// All optional values MUST NOT BE nil/zero after assured.
func assure(src, dest string, opts ...Options) Options {
defopt := getDefaultOptions(src, dest)
if len(opts) == 0 {
return defopt
}
if opts[0].OnSymlink == nil {
opts[0].OnSymlink = defopt.OnSymlink
}
if opts[0].Skip == nil {
opts[0].Skip = defopt.Skip
}
opts[0].intent.src = defopt.intent.src
opts[0].intent.dest = defopt.intent.dest
return opts[0]
}
// +build !windows
package copy
import (
"os"
"path/filepath"
"syscall"
)
// pcopy is for just named pipes
func pcopy(dest string, info os.FileInfo) error {
if err := os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil {
return err
}
return syscall.Mkfifo(dest, uint32(info.Mode()))
}
// +build windows
package copy
import (
"os"
)
// pcopy is for just named pipes. Windows doesn't support them
func pcopy(dest string, info os.FileInfo) error {
return nil
}
module github.com/otiai10/copy
go 1.14
require github.com/otiai10/mint v1.3.2
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
github.com/otiai10/curr v1.0.0 h1:TJIWdbX0B+kpNagQrjgq8bCMrbhiuX73M2XwgtDMoOI=
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
github.com/otiai10/mint v1.3.2 h1:VYWnrP5fXmz1MXvjuUvcBrXSjGE6xjON+axB/UrpO3E=
github.com/otiai10/mint v1.3.2/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
package copy
import "os"
// Options specifies optional actions on copying.
type Options struct {
// OnSymlink can specify what to do on symlink
OnSymlink func(src string) SymlinkAction
// OnDirExists can specify what to do when there is a directory already existing in destination.
OnDirExists func(src, dest string) DirExistsAction
// Skip can specify which files should be skipped
Skip func(src string) (bool, error)
// AddPermission to every entities,
// NO MORE THAN 0777
AddPermission os.FileMode
// Sync file after copy.
// Useful in case when file must be on the disk
// (in case crash happens, for example),
// at the expense of some performance penalty
Sync bool
// Preserve the atime and the mtime of the entries.
// On linux we can preserve only up to 1 millisecond accuracy.
PreserveTimes bool
// The byte size of the buffer to use for copying files.
// If zero, the internal default buffer of 32KB is used.
// See https://golang.org/pkg/io/#CopyBuffer for more information.
CopyBufferSize uint
intent struct {
src string
dest string
}
}
// SymlinkAction represents what to do on symlink.
type SymlinkAction int
const (
// Deep creates hard-copy of contents.
Deep SymlinkAction = iota
// Shallow creates new symlink to the dest of symlink.
Shallow
// Skip does nothing with symlink.
Skip
)
// DirExistsAction represents what to do on dest dir.
type DirExistsAction int
const (
// Merge preserves or overwrites existing files under the dir (default behavior).
Merge DirExistsAction = iota
// Replace deletes all contents under the dir and copy src files.
Replace
// Untouchable does nothing for the dir, and leaves it as it is.
Untouchable
)
// getDefaultOptions provides default options,
// which would be modified by usage-side.
func getDefaultOptions(src, dest string) Options {
return Options{
OnSymlink: func(string) SymlinkAction {
return Shallow // Do shallow copy
},
OnDirExists: nil, // Default behavior is "Merge".
Skip: func(string) (bool, error) {
return false, nil // Don't skip
},
AddPermission: 0, // Add nothing
Sync: false, // Do not sync
PreserveTimes: false, // Do not preserve the modification time
CopyBufferSize: 0, // Do not specify, use default bufsize (32*1024)
intent: struct {
src string
dest string
}{src, dest},
}
}
package copy
import "os"
func preserveTimes(srcinfo os.FileInfo, dest string) error {
spec := getTimeSpec(srcinfo)
if err := os.Chtimes(dest, spec.Atime, spec.Mtime); err != nil {
return err
}
return nil
}
// +build !windows,!darwin,!freebsd
// TODO: add more runtimes
package copy
import (
"os"
"syscall"
"time"
)
func getTimeSpec(info os.FileInfo) timespec {
stat := info.Sys().(*syscall.Stat_t)
times := timespec{
Mtime: info.ModTime(),
Atime: time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)),
Ctime: time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)),
}
return times
}
// +build darwin
package copy
import (
"os"
"syscall"
"time"
)
func getTimeSpec(info os.FileInfo) timespec {
stat := info.Sys().(*syscall.Stat_t)
times := timespec{
Mtime: info.ModTime(),
Atime: time.Unix(stat.Atimespec.Sec, stat.Atimespec.Nsec),
Ctime: time.Unix(stat.Ctimespec.Sec, stat.Ctimespec.Nsec),
}
return times
}
// +build freebsd
package copy
import (
"os"
"syscall"
"time"
)
func getTimeSpec(info os.FileInfo) timespec {
stat := info.Sys().(*syscall.Stat_t)
times := timespec{
Mtime: info.ModTime(),
Atime: time.Unix(int64(stat.Atimespec.Sec), int64(stat.Atimespec.Nsec)),
Ctime: time.Unix(int64(stat.Ctimespec.Sec), int64(stat.Ctimespec.Nsec)),
}
return times
}
// +build windows
package copy
import (
"os"
"syscall"
"time"
)
func getTimeSpec(info os.FileInfo) timespec {
stat := info.Sys().(*syscall.Win32FileAttributeData)
return timespec{
Mtime: time.Unix(0, stat.LastWriteTime.Nanoseconds()),
Atime: time.Unix(0, stat.LastAccessTime.Nanoseconds()),
Ctime: time.Unix(0, stat.CreationTime.Nanoseconds()),
}
}
// +build !windows
package copy
import (
"os"
"syscall"
"testing"
)
func setup(m *testing.M) {
os.MkdirAll("test/data.copy", os.ModePerm)
os.Symlink("test/data/case01", "test/data/case03/case01")
os.Chmod("test/data/case07/dir_0555", 0555)
os.Chmod("test/data/case07/file_0444", 0444)
syscall.Mkfifo("test/data/case11/foo/bar", 0555)
}
// +build windows
package copy
import (
"os"
"testing"
)
func setup(m *testing.M) {
os.MkdirAll("test/data.copy", os.ModePerm)
os.Symlink("test/data/case01", "test/data/case03/case01")
os.Chmod("test/data/case07/dir_0555", 0555)
os.Chmod("test/data/case07/file_0444", 0444)
}
......@@ -119,6 +119,12 @@ func (t *TLS) AddCN(secret *v1.Secret, cn ...string) (*v1.Secret, bool, error) {
return t.generateCert(secret, cn...)
}
func (t *TLS) Regenerate(secret *v1.Secret) (*v1.Secret, error) {
cns := cns(secret)
secret, _, err := t.generateCert(nil, cns...)
return secret, err
}
func (t *TLS) generateCert(secret *v1.Secret, cn ...string) (*v1.Secret, bool, error) {
secret = secret.DeepCopy()
if secret == nil {
......
......@@ -27,6 +27,7 @@ type TLSFactory interface {
AddCN(secret *v1.Secret, cn ...string) (*v1.Secret, bool, error)
Merge(target *v1.Secret, additional *v1.Secret) (*v1.Secret, bool, error)
Filter(cn ...string) []string
Regenerate(secret *v1.Secret) (*v1.Secret, error)
}
type SetFactory interface {
......@@ -74,11 +75,18 @@ func NewListener(l net.Listener, storage TLSStorage, caCert *x509.Certificate, c
setter.SetFactory(dynamicListener.factory)
}
if config.RegenerateCerts != nil && config.RegenerateCerts() {
if err := dynamicListener.regenerateCerts(); err != nil {
return nil, nil, err
}
}
if config.ExpirationDaysCheck == 0 {
config.ExpirationDaysCheck = 30
}
tlsListener := tls.NewListener(dynamicListener.WrapExpiration(config.ExpirationDaysCheck), dynamicListener.tlsConfig)
return tlsListener, dynamicListener.cacheHandler(), nil
}
......@@ -129,6 +137,7 @@ type Config struct {
MaxSANs int
ExpirationDaysCheck int
CloseConnOnCertChange bool
RegenerateCerts func() bool
FilterCN func(...string) []string
}
......@@ -180,6 +189,30 @@ func (l *listener) WrapExpiration(days int) net.Listener {
}
}
// regenerateCerts regenerates the used certificates and
// updates the secret.
func (l *listener) regenerateCerts() error {
l.Lock()
defer l.Unlock()
secret, err := l.storage.Get()
if err != nil {
return err
}
newSecret, err := l.factory.Regenerate(secret)
if err != nil {
return err
}
if err := l.storage.Update(newSecret); err != nil {
return err
}
// clear version to force cert reload
l.version = ""
return nil
}
func (l *listener) checkExpiration(days int) error {
l.Lock()
defer l.Unlock()
......@@ -347,7 +380,7 @@ func (l *listener) loadCert() (*tls.Certificate, error) {
if err != nil {
return nil, err
}
if l.cert != nil && l.version == secret.ResourceVersion {
if l.cert != nil && l.version == secret.ResourceVersion && secret.ResourceVersion != "" {
return l.cert, nil
}
......@@ -360,7 +393,7 @@ func (l *listener) loadCert() (*tls.Certificate, error) {
if err != nil {
return nil, err
}
if l.cert != nil && l.version == secret.ResourceVersion {
if l.cert != nil && l.version == secret.ResourceVersion && secret.ResourceVersion != "" {
return l.cert, nil
}
......
......@@ -841,6 +841,9 @@ github.com/opencontainers/runtime-spec/specs-go
github.com/opencontainers/selinux/go-selinux
github.com/opencontainers/selinux/go-selinux/label
github.com/opencontainers/selinux/pkg/pwalk
# github.com/otiai10/copy v1.6.0
## explicit
github.com/otiai10/copy
# github.com/peterbourgon/diskv v2.0.1+incompatible
github.com/peterbourgon/diskv
# github.com/pierrec/lz4 v2.5.2+incompatible
......@@ -871,7 +874,7 @@ github.com/prometheus/common/model
github.com/prometheus/procfs
github.com/prometheus/procfs/internal/fs
github.com/prometheus/procfs/internal/util
# github.com/rancher/dynamiclistener v0.2.2
# github.com/rancher/dynamiclistener v0.2.3-k3s1
## explicit
github.com/rancher/dynamiclistener
github.com/rancher/dynamiclistener/cert
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment