mirror of
https://github.com/containrrr/watchtower.git
synced 2024-12-12 09:04:17 +02:00
Registry authentication was failing silently when pulling images.
Load authentication credentials for available credential stores in order of preference: 1. Environment variables REPO_USER, REPO_PASS 2. Docker config files Request image pull with authentication header. Wait until pull request is complete before exiting function.
This commit is contained in:
parent
ef430b791a
commit
1c59200565
10
README.md
10
README.md
@ -39,6 +39,16 @@ docker run -d \
|
||||
centurylink/watchtower
|
||||
```
|
||||
|
||||
If pulling images from a private Docker registry, supply any authentication credentials with the environment variables `REPO_USER` and `REPO_PASS` or omit to leave watchtower load credentials from the default Docker config (`~/.docker/config.json`):
|
||||
|
||||
```
|
||||
docker run -d \
|
||||
--name watchtower \
|
||||
-e REPO_USER="<username>" -e REPO_PASS="<password>" \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
drud/watchtower container_to_watch --debug
|
||||
```
|
||||
|
||||
### Arguments
|
||||
|
||||
By default, watchtower will monitor all containers running within the Docker daemon to which it is pointed (in most cases this will be the local Docker daemon, but you can override it with the `--host` option described in the next section). However, you can restrict watchtower to monitoring a subset of the running containers by specifying the container names as arguments when launching watchtower.
|
||||
|
@ -3,11 +3,11 @@ package container
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"io/ioutil"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
dockerclient "github.com/docker/docker/client"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/cli/command"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
@ -146,22 +146,28 @@ func (client dockerClient) IsContainerStale(c Container) (bool, error) {
|
||||
if client.pullImages {
|
||||
log.Debugf("Pulling %s for %s", imageName, c.Name())
|
||||
|
||||
auth := types.AuthConfig {
|
||||
Username: "testuser",
|
||||
Password: "testpassword",
|
||||
}
|
||||
encodedAuth, err := command.EncodeAuthToBase64(auth)
|
||||
var opts types.ImagePullOptions // ImagePullOptions can take a RegistryAuth arg to authenticate against a private registry
|
||||
auth, err := EncodedEnvAuth(imageName)
|
||||
if err != nil {
|
||||
return false, err
|
||||
// credentials environment vars not set, trying Docker config instead
|
||||
auth, err = EncodedConfigAuth(imageName)
|
||||
}
|
||||
if err != nil {
|
||||
log.Debug("No authentication credentials found")
|
||||
opts = types.ImagePullOptions{}
|
||||
} else {
|
||||
opts = types.ImagePullOptions{RegistryAuth: auth, PrivilegeFunc: DefaultAuthHandler}
|
||||
}
|
||||
|
||||
// Note: ImagePullOptions below can take a RegistryAuth arg if 401 on private registry
|
||||
closer, err := client.api.ImagePull(bg, imageName, types.ImagePullOptions{RegistryAuth: encodedAuth})
|
||||
response, err := client.api.ImagePull(bg, imageName, opts)
|
||||
if err != nil {
|
||||
log.Debugf("Error pulling image %s, %s", imageName, err)
|
||||
return false, err
|
||||
}
|
||||
defer closer.Close()
|
||||
defer response.Close()
|
||||
|
||||
// the pull request will be aborted prematurely unless the response is read
|
||||
_, err = ioutil.ReadAll(response)
|
||||
}
|
||||
|
||||
newImageInfo, _, err := client.api.ImageInspectWithRaw(bg, imageName)
|
||||
@ -174,7 +180,6 @@ func (client dockerClient) IsContainerStale(c Container) (bool, error) {
|
||||
return true, nil
|
||||
} else {
|
||||
log.Debugf("No new images found for %s", c.Name())
|
||||
log.Debugf("Old image ID %s is the same as New Image ID %s", oldImageInfo.ID, newImageInfo.ID)
|
||||
}
|
||||
|
||||
|
||||
|
93
container/trust.go
Normal file
93
container/trust.go
Normal file
@ -0,0 +1,93 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"strings"
|
||||
"fmt"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/reference"
|
||||
"github.com/docker/docker/cli/command"
|
||||
"github.com/docker/docker/cliconfig/configfile"
|
||||
"github.com/docker/docker/cliconfig/credentials"
|
||||
)
|
||||
|
||||
|
||||
/*
|
||||
* Return an encoded auth config for the given registry
|
||||
* hardcoded for a test environment
|
||||
*/
|
||||
func EncodedTestAuth(ref string) (string, error) {
|
||||
auth := types.AuthConfig {
|
||||
Username: "testuser",
|
||||
Password: "testpassword",
|
||||
}
|
||||
return EncodeAuth(auth)
|
||||
}
|
||||
|
||||
/*
|
||||
* Return an encoded auth config for the given registry
|
||||
* loaded from environment variables
|
||||
*/
|
||||
func EncodedEnvAuth(ref string) (string, error) {
|
||||
username := os.Getenv("REPO_USER")
|
||||
password := os.Getenv("REPO_PASS")
|
||||
if username != "" && password != "" {
|
||||
auth := types.AuthConfig {
|
||||
Username: username,
|
||||
Password: password,
|
||||
}
|
||||
log.Debugf("Loaded auth credentials %s from environment for %s", auth, ref)
|
||||
return EncodeAuth(auth)
|
||||
} else {
|
||||
return "", errors.New("Registry auth environment variables (REPO_USER, REPO_PASS) not set")
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return an encoded auth config for the given registry
|
||||
* loaded from the docker config
|
||||
*/
|
||||
func EncodedConfigAuth(ref string) (string, error) {
|
||||
server, err := ParseServerAddress(ref)
|
||||
configFile := command.LoadDefaultConfigFile(log.StandardLogger().Out)
|
||||
credStore := CredentialsStore(*configFile)
|
||||
auth, err := credStore.Get(server)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
log.Debugf("Loaded auth credentials %s from Docker config for reference %s", auth, ref)
|
||||
return EncodeAuth(auth)
|
||||
}
|
||||
|
||||
func ParseServerAddress(ref string) (string, error) {
|
||||
repository, _, err := reference.Parse(ref)
|
||||
if err != nil {
|
||||
return ref, err
|
||||
}
|
||||
parts := strings.Split(repository, "/")
|
||||
return parts[0], nil
|
||||
|
||||
}
|
||||
|
||||
// CredentialsStore returns a new credentials store based
|
||||
// on the settings provided in the configuration file.
|
||||
func CredentialsStore(configFile configfile.ConfigFile) credentials.Store {
|
||||
if configFile.CredentialsStore != "" {
|
||||
return credentials.NewNativeStore(&configFile)
|
||||
}
|
||||
return credentials.NewFileStore(&configFile)
|
||||
}
|
||||
|
||||
/*
|
||||
* Base64 encode an AuthConfig struct for transmission over HTTP
|
||||
*/
|
||||
func EncodeAuth(auth types.AuthConfig) (string, error) {
|
||||
return command.EncodeAuthToBase64(auth)
|
||||
}
|
||||
|
||||
func DefaultAuthHandler() (string, error) {
|
||||
log.Error("Authentication requested")
|
||||
return "", fmt.Errorf("Error requesting privilege")
|
||||
}
|
18
main.go
18
main.go
@ -15,10 +15,10 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
client container.Client
|
||||
pollInterval time.Duration
|
||||
cleanup bool
|
||||
wg sync.WaitGroup
|
||||
client container.Client
|
||||
pollInterval time.Duration
|
||||
cleanup bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -61,8 +61,8 @@ func main() {
|
||||
Usage: "enable debug mode with verbose logging",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "apiversion",
|
||||
Usage: "the version of the docker api",
|
||||
Name: "apiversion",
|
||||
Usage: "the version of the docker api",
|
||||
EnvVar: "DOCKER_API_VERSION",
|
||||
},
|
||||
}
|
||||
@ -124,9 +124,9 @@ func handleSignals() {
|
||||
}
|
||||
|
||||
func setEnvOptStr(env string, opt string) error {
|
||||
if (opt != "" && opt != os.Getenv(env)) {
|
||||
if opt != "" && opt != os.Getenv(env) {
|
||||
err := os.Setenv(env, opt)
|
||||
if (err != nil) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -134,7 +134,7 @@ func setEnvOptStr(env string, opt string) error {
|
||||
}
|
||||
|
||||
func setEnvOptBool(env string, opt bool) error {
|
||||
if (opt == true) {
|
||||
if opt == true {
|
||||
return setEnvOptStr(env, "1")
|
||||
}
|
||||
return nil
|
||||
|
Loading…
Reference in New Issue
Block a user