forked from OpenCloud/opencloud
30 changed files with 1338 additions and 804 deletions
@ -1,6 +1,6 @@
|
||||
package installd |
||||
|
||||
func GetPathOfBootEnv(name string) (path string){ |
||||
func GetPathOfBootEnv(name string) (path string) { |
||||
//TODO get Path via beadm
|
||||
return name |
||||
} |
||||
|
@ -1,151 +0,0 @@
|
||||
package keystore |
||||
|
||||
import ( |
||||
"fmt" |
||||
"io/ioutil" |
||||
"os" |
||||
) |
||||
|
||||
var sampleKeys = []string{ |
||||
// 3ofcoins.net provisional key
|
||||
`-----BEGIN PGP PUBLIC KEY BLOCK----- |
||||
Version: GnuPG v2 |
||||
mQENBFVnXGIBCADovl1Yw/Ftjz7QZ9qVEUmJ9ztduws50yk+RbY+R1UJQvLhWU10 |
||||
izORyN1qrXTYrQKoiJDJ9eTc7WHqrJGsNrgZWGc5SvMTdfVtiRdfaKP5QrIgXI5/ |
||||
EZwZjRgi3ty/hsq2RvpNkvC5cXp4sSYf63dEFFLD4Ps3G+Lc4adKyNn8gZSieHjv |
||||
0+aizg7DW+mqNxUy3NK5wkRo876EYbhVpGCZRxPq39p88wqb6j1Rkt68jNtMOWkA |
||||
euoCxfeXR9jbU7ArNFxmhg6/ND7Zq60PAC5aImBLolXXMAK62cBPJBTZulNXSNxF |
||||
iZZBtFl5COyQNHSwbZUOv4jdrOiwE894qC8NABEBAAG0MkFDSSAzb2Zjb2lucy5u |
||||
ZXQgKHByb3Zpc2lvbmFsKSA8aW5mb0Azb2Zjb2lucy5uZXQ+iQE3BBMBCAAhBQJV |
||||
Z1xiAhsDBQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJEFPMwtY6FiZkYXEIAJiM |
||||
bGvBOIrjIKhW0y6syRIGuypkJw7dhp9EvMwzsvMLM86ze7iX2gNaBOGKWMdIRAYH |
||||
gKLN+f65ZOviOY5yy6fMG5A9ySlSWIKiLPDX6X/pXwMhmSUBkwhWKUWXIHd0+3d4 |
||||
lGUWzzimTFfjWNlPWbHb08HMepMuNtCxtKkxvRgpH0yaWNCHQe3NsWAh6nYLsXpJ |
||||
A/2lomQ9K/qhHyZT18eYD1lHCpEOPksqifv5BKAFpXekGsFjTbYIKj6bgspaMg+d |
||||
GHMg3KUnCulgzhhbo0bMyMqrHm8waSF/JabTiIqp/ePFiUWqeirBdZ8yje6TA9eS |
||||
7UMOx+veKUT6Y5axbPaJARsEEAEIAAYFAlVnXLYACgkQRjqg8fR27d7nsAf3cxHv |
||||
Q+ljIyTqzxHDCgMmMLFPd8zzZ6cBTUYgb8R2G8EjYVg5Qu9zwv3FOZWaY19hBYDP |
||||
AzXjQtjkdQiwGvOppgui4CmnjJpNlyPd5J2A06fyeg2F28WJacrxUdTre1/UwN+c |
||||
9UUJo9o3OluVbaq1o9rXJnau79HOqWDoh8VdTzZAd3VO4SXuI7GUP49j6NC1t8FO |
||||
I7Qe0TLHzkZDeiZmVlK5OhfwhzMBQ5Qjv/9iT47Od91ryXKrHNAICyT32K4zawc0 |
||||
/xs0oAQb5MOE4HrxW46e8UCT+2u+GrtS5ftWQqAMqnBF9oiRpSfXis3XgFyP5nU6 |
||||
+ZaUESTObMoZyZ67uQENBFVnXGIBCADE19yLRCcO0NNBvDxWVFdFLsDKzl7vAQWm |
||||
cjaWBPJi0pWbi+vppECSjf8S5NNUXqx8MObG4i+ivBR4dqPbVls7Lpzn9wt3MwPZ |
||||
RbCLPLvBPJtlYtIt76JlqcjuJws3kd5hmX/1gny8Vy9RoNWt4y1ldde4+mJXGHoW |
||||
OY3CHj1vagkRE445czYT+ST8xeSxLKAyAEHis3U6Gb80uHbRknt/bUaogbibrl9N |
||||
3MyRX56cctVqpY8bJTi1PDKpSNcUHT+LMQ6rpPc2anKVqoUHT9Ye6hLSvNeBwJHs |
||||
LLWEWNRfmjjZvHs4hfzzaUVT0bP2IMkMBpxMCAOEargaJYhD1olFABEBAAGJAR8E |
||||
GAEIAAkFAlVnXGICGwwACgkQU8zC1joWJmTROAgA4RBWZww5CaBZ2ZbBijNRK8sa |
||||
swUrdxxcAkRzki2Z5KpBXESZEe6iPZt/rsGNw0F2XaORP+DRrzTGg+DSQlh5nT2z |
||||
LFVkNyeW3RJQjP2K2avikjRgoprryr/WVUOTpk4jK/MpBxUU0Htpvks2Jly2vYJD |
||||
Z9R2yW31RLEJXcVxOR/LcW6UfkjhOsH/RbKbcMx+HraxKk7jmrKZEglz8cO0BMGc |
||||
gemHpMb04W4YXOdKjqppm0kS+TDTEm85xnfAe14olr3ZsHn/ey8nc3JEb3aCBeuC |
||||
PjkMkIAW7BYX1ZKyV9z5EQVkqkz7zwyZYO1/tmdjQQH/x+mD0MwpeIF0E78YhA== |
||||
=m9io |
||||
-----END PGP PUBLIC KEY BLOCK----- |
||||
`, |
||||
|
||||
// quay.io signing key
|
||||
`-----BEGIN PGP PUBLIC KEY BLOCK----- |
||||
Version: GnuPG v2 |
||||
mQENBFTT6doBCACkVncI+t4HASQdnByRlXCYkwjsPqGOlgTCgenop5I6vgTqFWhQ |
||||
PMNhtSaFdFECMt2WKQT4QGVbfVOmIH9CLV+Muqvk4iJIAn3Nh3qp/kfMhwjGaS6m |
||||
fWN2ARFCq4RIs9tboCNQOouaD5C26/FsQtIsoqyYcdX+YFaU1a+R1kp0fc2CABDI |
||||
k6Iq8oEJO+FOYvqQYIJNfd3c0NHICilMu2jO3yIsw80qzWoFAAblyb0zVq/hudWB |
||||
4vdVzPmJe1f4Ymk8l1R413bN65LcbCiOax3hmFWovJoxlkL7WoGTTMfaeb2QmaPL |
||||
qcu4Q94v1KG87gyxbkIo5uZdvMLdswQI7yQ7ABEBAAG0RFF1YXkuaW8gQUNJIENv |
||||
bnZlcnRlciAoQUNJIGNvbnZlcnNpb24gc2lnbmluZyBrZXkpIDxzdXBwb3J0QHF1 |
||||
YXkuaW8+iQE5BBMBAgAjBQJU0+naAhsDBwsJCAcDAgEGFQgCCQoLBBYCAwECHgEC |
||||
F4AACgkQcqv19nmdM7zKzggAjGFqy7Hcx6TCFXn53/inl5iyKrTu8cuF4K547XuZ |
||||
12Dt8b6PgJ+b3z6UnMMTd0wXKGcfOmNeQ2R71xmVnviuo7xB5ZkZIBxHI4M/5uhK |
||||
I6GZKr84WJS2ec7ssH2ofFQ5u1l+es9jUwW0KbAoNmES0IcdDy28xfmJpkfOn3oI |
||||
P2Bzz4rGlIqJXEjq28Wk+qQu64kJRKYuPNXqiHncPDm+i5jMXUUN1D+pkDukp26x |
||||
oLbpol42/jIcM3fe2AFZnflittBCHYLIHjJ51NlpSHJZmf2pQZbdyeKElN2SCNe7 |
||||
nDcol24zYIC+SX0K23w/LrLzlff4mzbO99ePt1bB9zAiVA== |
||||
=SBoV |
||||
-----END PGP PUBLIC KEY BLOCK----- |
||||
`, |
||||
|
||||
// coreos.com/etcd
|
||||
`-----BEGIN PGP PUBLIC KEY BLOCK----- |
||||
Version: GnuPG v1 |
||||
mQINBFTCnMQBEAC/49bGbStCpa3peej+/42mobfuGbTcdmcGGwYZmigP0Kl0TPZK |
||||
zcIxhVZOr3ITYuAx8T1WWJVb7/r/y4roUogDrUSTm2nAbLP8xp8Qn/N1zaXFyEtJ |
||||
WTaLuPI2mq643x8g7fAiJY19JRjFbYVVZLr5OMOvHrOdtYVN31ARZeSxmqP5yFNW |
||||
9DgoXG0/w80EOXIsWWoJgjaKLye15LHI86MjPthXyT5K212efxPffSk1hca04Dmk |
||||
I5vCMHC1Q2DbrlilhS0DTf+lSK2YgkaHPWiNSZb3XvjwbU8qQMzyfnQcQrQlfm4/ |
||||
1fHLj2bWyASzNG/MOJCQ2JyEyIzbS2M4jKcfFxaKKuJA5PwdfjbRTkvxAKbFcdc5 |
||||
ER7D3QoEOxgRDMppHaihKNI/T4dPIuqyUczq3ia9fGfrQFROAIxdAnBqzzBVaetv |
||||
FYFVjJlAhGsWWEhuO3P7qGwwR7CtPkWvvsMT8CYdHP2h7uOrOZioGCQ+09YBGpJ9 |
||||
LzwCKHiV2s4/aBVfLhjttGqXG+PW/Kzg5rtwAjSdeooThKQLQk/ok1TtFydgNVNH |
||||
kSPNdhgiTWlNmK8Qj3C1zqZmcPzv+c6y6f79GTL0+Hz6gqnMGdIpFJtmbusU79/2 |
||||
MkDqumBAslvwm7h85s0ccKwZCG1VelyhGLawVyxin0UhLWlzYd6SL5IuuQARAQAB |
||||
tCdDb3JlT1MgQUNJIEJ1aWxkZXIgPHJlbGVhc2VAY29yZW9zLmNvbT6JAjgEEwEC |
||||
ACIFAlTCnMQCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEFIQvYiIGCGQ |
||||
nxYP+wcVClXD1t5oT7mwvYZPfF9/+itOSHvN6++T4CCFkRVdIN7/G/Ou1wRb/fV+ |
||||
P27Rc7gnK+jbQJqUa8aEsNSWZT/1A8VaQ51orQdV80ZROzrJPLBB0w4fkEsSESO+ |
||||
Uuz9ZsiEOhZf0ATkafrF1jfepGXmoHJxLJ+bKS+KlAEhtceB1jiWAafGPy99XUAf |
||||
MBJ905F6bXvDqQov+9U3vyUGnwA6ymCCqKIoCVx3GdKOh5UaqC8pHaF7oI7xkv5b |
||||
5WMajzuXwKBy6KoduHTnW7Y5g1aoGwW5FDFoEq4LsBvcxeUI6OMVEWCVe502E+4S |
||||
lWx2gEvFa33wy77kYp2ZvHToY5tSjiIi8QocR0IgfLqE3P1ZMPe9YXk5EveEZH6Q |
||||
VtQ8z1ktuZCVQqrtTEeernEdSFsTVFSoWUsNJV1FMlgisZZ0ljoFJrH7/7GkYhK9 |
||||
DT7OcrZnyZDUkEIiVaqWwjWw5Ing4IHExAq+PlXDwrA0QcH2UW9IurDCvPiXHpxi |
||||
D0V21oEbANUdGtYcOSDRBbZlsonINJZQ1Ad2XrY8kfZ2sZXAZuBZbH0+dEx6zmua |
||||
C0IGN3SLFseScqJZ5G1joYYqOKOUweErkzA/62Kaj31SVoQDpZyMqtwTjjZaFT8N |
||||
fMkBtaM3knaFonHZc19BD1FOishRThCCq2Ty8HUoN2Fk7w0l |
||||
=bYl7 |
||||
-----END PGP PUBLIC KEY BLOCK-----`, |
||||
} |
||||
|
||||
var sampleKeyFingerprints = []string{ |
||||
"4706dc5d5c214bc3ad127c6d53ccc2d63a162664", |
||||
"bff313cdaa560b16a8987b8f72abf5f6799d33bc", |
||||
"8b86de38890ddb7291867b025210bd8888182190", |
||||
} |
||||
|
||||
func asFile(args ...interface{}) (_ *os.File, erv error) { |
||||
if f, err := ioutil.TempFile("", "jetpack.test.keystore."); err != nil { |
||||
return nil, err |
||||
} else { |
||||
defer func() { |
||||
if erv != nil { |
||||
f.Close() |
||||
} |
||||
}() |
||||
|
||||
if err := os.Remove(f.Name()); err != nil { |
||||
return nil, err |
||||
} |
||||
if _, err := fmt.Fprint(f, args...); err != nil { |
||||
return nil, err |
||||
} |
||||
if _, err := f.Seek(0, 0); err != nil { |
||||
return nil, err |
||||
} |
||||
return f, nil |
||||
} |
||||
} |
||||
|
||||
var openedSampleKeys = make(map[int]*os.File) |
||||
|
||||
func openSampleKey(i int) *os.File { |
||||
if f := openedSampleKeys[i]; f != nil { |
||||
if _, err := f.Seek(0, 0); err != nil { |
||||
panic(err) |
||||
} |
||||
return f |
||||
} |
||||
if f, err := asFile(sampleKeys[i]); err != nil { |
||||
panic(err) |
||||
} else { |
||||
openedSampleKeys[i] = f |
||||
return f |
||||
} |
||||
} |
||||
|
||||
func closeSampleKeys() { |
||||
for i, f := range openedSampleKeys { |
||||
f.Close() |
||||
delete(openedSampleKeys, i) |
||||
} |
||||
} |
@ -1,42 +0,0 @@
|
||||
package keystore |
||||
|
||||
import ( |
||||
"fmt" |
||||
"path/filepath" |
||||
"sort" |
||||
"strings" |
||||
|
||||
"github.com/appc/spec/schema/types" |
||||
"golang.org/x/crypto/openpgp" |
||||
) |
||||
|
||||
type Entity struct { |
||||
*openpgp.Entity |
||||
Path string |
||||
Prefix types.ACIdentifier |
||||
} |
||||
|
||||
func (e *Entity) Fingerprint() string { |
||||
return filepath.Base(e.Path) |
||||
} |
||||
|
||||
func (e *Entity) String() string { |
||||
identities := make([]string, 0, len(e.Entity.Identities)) |
||||
for name := range e.Entity.Identities { |
||||
identities = append(identities, name) |
||||
} |
||||
sort.Strings(identities) |
||||
return fmt.Sprintf("%v\t%v\t%v", e.Prefix, e.Fingerprint(), strings.Join(identities, "; ")) |
||||
} |
||||
|
||||
type EntityList []Entity |
||||
|
||||
// sort.Interface
|
||||
func (ee EntityList) Len() int { return len(ee) } |
||||
func (ee EntityList) Less(i, j int) bool { |
||||
if ee[i].Prefix == ee[j].Prefix { |
||||
return ee[i].Path < ee[j].Path |
||||
} |
||||
return ee[i].Prefix.String() < ee[j].Prefix.String() |
||||
} |
||||
func (ee EntityList) Swap(i, j int) { ee[i], ee[j] = ee[j], ee[i] } |
@ -1,71 +0,0 @@
|
||||
package keystore |
||||
|
||||
import ( |
||||
"fmt" |
||||
"os" |
||||
"path/filepath" |
||||
|
||||
"github.com/appc/spec/schema/types" |
||||
"golang.org/x/crypto/openpgp" |
||||
) |
||||
|
||||
type Keyring struct { |
||||
openpgp.EntityList |
||||
paths []string |
||||
prefixes []types.ACIdentifier |
||||
} |
||||
|
||||
func (kr *Keyring) loadFile(path string) error { |
||||
trustedKey, err := os.Open(path) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
defer trustedKey.Close() |
||||
entityList, err := openpgp.ReadArmoredKeyRing(trustedKey) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
if len(entityList) < 1 { |
||||
return fmt.Errorf("missing opengpg entity") |
||||
} |
||||
fingerprint := fingerprintToFilename(entityList[0].PrimaryKey.Fingerprint) |
||||
keyFile := filepath.Base(trustedKey.Name()) |
||||
if fingerprint != keyFile { |
||||
return fmt.Errorf("fingerprint mismatch: %q:%q", keyFile, fingerprint) |
||||
} |
||||
|
||||
prefix, err := pathToACIdentifier(path) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
kr.EntityList = append(kr.EntityList, entityList[0]) |
||||
kr.paths = append(kr.paths, path) |
||||
kr.prefixes = append(kr.prefixes, prefix) |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (kr *Keyring) Entities() EntityList { |
||||
rv := make(EntityList, len(kr.EntityList)) |
||||
for i, e := range kr.EntityList { |
||||
rv[i] = Entity{e, kr.paths[i], kr.prefixes[i]} |
||||
} |
||||
return rv |
||||
} |
||||
|
||||
// sort.Interface - sort by prefix, then by path
|
||||
func (kr *Keyring) Len() int { return len(kr.EntityList) } |
||||
|
||||
func (kr *Keyring) Less(i, j int) bool { |
||||
if kr.prefixes[i] == kr.prefixes[j] { |
||||
return kr.paths[i] < kr.paths[j] |
||||
} |
||||
return kr.prefixes[i].String() < kr.prefixes[j].String() |
||||
} |
||||
|
||||
func (kr *Keyring) Swap(i, j int) { |
||||
kr.EntityList[i], kr.EntityList[j] = kr.EntityList[j], kr.EntityList[i] |
||||
kr.paths[i], kr.paths[j] = kr.paths[j], kr.paths[i] |
||||
kr.prefixes[i], kr.prefixes[j] = kr.prefixes[j], kr.prefixes[i] |
||||
} |
@ -1,153 +1,390 @@
|
||||
package keystore |
||||
|
||||
// Heavily based on https://github.com/coreos/rkt/blob/master/pkg/keystore/keystore.go
|
||||
// Copyright 2014 The rkt Authors
|
||||
//
|
||||
// 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.
|
||||
|
||||
// We don't use rkt's keystore, because we want to escape ACIdentifier to
|
||||
// avoid path traversal issues and not to worry about prefix
|
||||
// collisions.
|
||||
// Package keystore implements the ACI keystore.
|
||||
package keystore |
||||
|
||||
import ( |
||||
"bytes" |
||||
"errors" |
||||
"fmt" |
||||
"io" |
||||
"io/ioutil" |
||||
"os" |
||||
"path" |
||||
"path/filepath" |
||||
"strings" |
||||
|
||||
"golang.org/x/crypto/openpgp" |
||||
|
||||
"git.wegmueller.it/opencloud/opencloud/common" |
||||
"github.com/appc/spec/schema/types" |
||||
"golang.org/x/crypto/openpgp" |
||||
) |
||||
|
||||
// Intentionally invalid ACIdentifier to mark root key
|
||||
const Root = types.ACIdentifier("@") |
||||
// A Config structure is used to configure a Keystore.
|
||||
type Config struct { |
||||
LocalRootPath string |
||||
LocalPrefixPath string |
||||
SystemRootPath string |
||||
SystemPrefixPath string |
||||
} |
||||
|
||||
// A Keystore represents a repository of trusted public keys which can be
|
||||
// used to verify PGP signatures.
|
||||
type Keystore struct { |
||||
Path string |
||||
*Config |
||||
} |
||||
|
||||
func New(path string) *Keystore { |
||||
return &Keystore{path} |
||||
// New returns a new Keystore based on config.
|
||||
func New(config *Config) *Keystore { |
||||
if config == nil { |
||||
config = defaultConfig |
||||
} |
||||
return &Keystore{config} |
||||
} |
||||
|
||||
func (ks *Keystore) prefixPath(prefix types.ACIdentifier) string { |
||||
if prefix.Empty() { |
||||
panic("Empty prefix!") |
||||
func NewConfig(systemPath, localPath string) *Config { |
||||
return &Config{ |
||||
LocalRootPath: filepath.Join(localPath, "trustedkeys", "root.d"), |
||||
LocalPrefixPath: filepath.Join(localPath, "trustedkeys", "prefix.d"), |
||||
SystemRootPath: filepath.Join(systemPath, "trustedkeys", "root.d"), |
||||
SystemPrefixPath: filepath.Join(systemPath, "trustedkeys", "prefix.d"), |
||||
} |
||||
return filepath.Join(ks.Path, strings.Replace(string(prefix), "/", ",", -1)) |
||||
} |
||||
|
||||
func (ks *Keystore) StoreTrustedKey(prefix types.ACIdentifier, key *os.File, acceptFingerprint string) (string, error) { |
||||
if prefix.Empty() { |
||||
panic("Empty prefix!") |
||||
var defaultConfig = NewConfig(common.DefaultSystemConfigDir, common.DefaultLocalConfigDir) |
||||
|
||||
// CheckSignature is a convenience method for creating a Keystore with a default
|
||||
// configuration and invoking CheckSignature.
|
||||
func CheckSignature(prefix string, signed, signature io.ReadSeeker) (*openpgp.Entity, error) { |
||||
ks := New(defaultConfig) |
||||
return checkSignature(ks, prefix, signed, signature) |
||||
} |
||||
|
||||
// CheckSignature takes a signed file and a detached signature and returns the signer
|
||||
// if the signature is signed by a trusted signer.
|
||||
// If the signer is unknown or not trusted, opengpg.ErrUnknownIssuer is returned.
|
||||
func (ks *Keystore) CheckSignature(prefix string, signed, signature io.ReadSeeker) (*openpgp.Entity, error) { |
||||
return checkSignature(ks, prefix, signed, signature) |
||||
} |
||||
|
||||
func checkSignature(ks *Keystore, prefix string, signed, signature io.ReadSeeker) (*openpgp.Entity, error) { |
||||
acidentifier, err := types.NewACIdentifier(prefix) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
keyring, err := ks.loadKeyring(acidentifier.String()) |
||||
if err != nil { |
||||
return nil, fmt.Errorf("keystore: error loading keyring: %s", err) |
||||
} |
||||
entities, err := openpgp.CheckArmoredDetachedSignature(keyring, signed, signature) |
||||
if err == io.EOF { |
||||
// When the signature is binary instead of armored, the error is io.EOF.
|
||||
// Let's try with binary signatures as well
|
||||
if _, err := signed.Seek(0, 0); err != nil { |
||||
return nil, fmt.Errorf("error seeking ACI file: %s", err) |
||||
} |
||||
if _, err := signature.Seek(0, 0); err != nil { |
||||
return nil, fmt.Errorf("error seeking signature file: %s", err) |
||||
} |
||||
entities, err = openpgp.CheckDetachedSignature(keyring, signed, signature) |
||||
} |
||||
if err == io.EOF { |
||||
// otherwise, the client failure is just "EOF", which is not helpful
|
||||
return nil, fmt.Errorf("keystore: no valid signatures found in signature file") |
||||
} |
||||
return entities, err |
||||
} |
||||
|
||||
if accepted, err := reviewKey(prefix, key, acceptFingerprint); err != nil { |
||||
return "", err |
||||
} else if !accepted { |
||||
return "", nil |
||||
// DeleteTrustedKeyPrefix deletes the prefix trusted key identified by fingerprint.
|
||||
func (ks *Keystore) DeleteTrustedKeyPrefix(prefix, fingerprint string) error { |
||||
acidentifier, err := types.NewACIdentifier(prefix) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
return os.Remove(path.Join(ks.LocalPrefixPath, acidentifier.String(), fingerprint)) |
||||
} |
||||
|
||||
pubkeyBytes, err := ioutil.ReadAll(key) |
||||
// MaskTrustedKeySystemPrefix masks the system prefix trusted key identified by fingerprint.
|
||||
func (ks *Keystore) MaskTrustedKeySystemPrefix(prefix, fingerprint string) (string, error) { |
||||
acidentifier, err := types.NewACIdentifier(prefix) |
||||
if err != nil { |
||||
return "", err |
||||
} |
||||
|
||||
dir := ks.prefixPath(prefix) |
||||
if err := os.MkdirAll(dir, 0750); err != nil { |
||||
dst := path.Join(ks.LocalPrefixPath, acidentifier.String(), fingerprint) |
||||
if err := ioutil.WriteFile(dst, []byte(""), 0644); err != nil { |
||||
return "", err |
||||
} |
||||
|
||||
entityList, err := openpgp.ReadArmoredKeyRing(bytes.NewReader(pubkeyBytes)) |
||||
if err != nil { |
||||
if err := os.Chmod(dst, 0644); err != nil { |
||||
return "", err |
||||
} |
||||
return dst, nil |
||||
} |
||||
|
||||
// FIXME: cargo cult from rkt
|
||||
// FIXME: can we import more than one key here, and note only one?
|
||||
// Maybe we should split and re-armor the entityList?
|
||||
trustedKeyPath := filepath.Join(dir, fingerprintToFilename(entityList[0].PrimaryKey.Fingerprint)) |
||||
// DeleteTrustedKeyRoot deletes the root trusted key identified by fingerprint.
|
||||
func (ks *Keystore) DeleteTrustedKeyRoot(fingerprint string) error { |
||||
return os.Remove(path.Join(ks.LocalRootPath, fingerprint)) |
||||
} |
||||
|
||||
if err := ioutil.WriteFile(trustedKeyPath, pubkeyBytes, 0640); err != nil { |
||||
// MaskTrustedKeySystemRoot masks the system root trusted key identified by fingerprint.
|
||||
func (ks *Keystore) MaskTrustedKeySystemRoot(fingerprint string) (string, error) { |
||||
dst := path.Join(ks.LocalRootPath, fingerprint) |
||||
if err := ioutil.WriteFile(dst, []byte(""), 0644); err != nil { |
||||
return "", err |
||||
} |
||||
|
||||
return trustedKeyPath, nil |
||||
if err := os.Chmod(dst, 0644); err != nil { |
||||
return "", err |
||||
} |
||||
return dst, nil |
||||
} |
||||
|
||||
func (ks *Keystore) UntrustKey(fingerprint string) (removed []types.ACIdentifier, err error) { |
||||
err = ks.walk("", func(prefix types.ACIdentifier, path string) error { |
||||
if filepath.Base(path) == fingerprint { |
||||
if err := os.Remove(path); err != nil { |
||||
return err |
||||
// TrustKeyPrefixExists returns whether or not there exists 1 or more trusted
|
||||
// keys for a given prefix, or for any parent prefix.
|
||||
func (ks *Keystore) TrustedKeyPrefixExists(prefix string) (bool, error) { |
||||
acidentifier, err := types.NewACIdentifier(prefix) |
||||
if err != nil { |
||||
return false, err |
||||
} |
||||
|
||||
pathNamesPrefix := []string{ |
||||
// example: /etc/rkt/trustedkeys/prefix.d/coreos.com/etcd
|
||||
path.Join(ks.LocalPrefixPath, acidentifier.String()), |
||||
// example: /usr/lib/rkt/trustedkeys/prefix.d/coreos.com/etcd
|
||||
path.Join(ks.SystemPrefixPath, acidentifier.String()), |
||||
} |
||||
|
||||
for _, p := range pathNamesPrefix { |
||||
_, err := os.Stat(p) |
||||
if os.IsNotExist(err) { |
||||
continue |
||||
} |
||||
if err != nil { |
||||
return false, fmt.Errorf("cannot check dir %q: %s", p, err) |
||||
} |
||||
files, err := ioutil.ReadDir(p) |
||||
if err != nil { |
||||
return false, fmt.Errorf("cannot list files in dir %q: %s", p, err) |
||||
} |
||||
for _, f := range files { |
||||
if !f.IsDir() && f.Size() > 0 { |
||||
return true, nil |
||||
} |
||||
removed = append(removed, prefix) |
||||
} |
||||
return nil |
||||
}) |
||||
return |
||||
} |
||||
|
||||
parentPrefix, _ := path.Split(prefix) |
||||
parentPrefix = strings.Trim(parentPrefix, "/") |
||||
|
||||
if parentPrefix != "" { |
||||
return ks.TrustedKeyPrefixExists(parentPrefix) |
||||
} |
||||
|
||||
return false, nil |
||||
} |
||||
|
||||
func (ks *Keystore) walk(name types.ACIdentifier, fn func(prefix types.ACIdentifier, path string) error) error { |
||||
var namePath string |
||||
if !name.Empty() { |
||||
namePath = ks.prefixPath(name) |
||||
// TrustedKeyPrefixWithFingerprintExists returns whether or not a trusted key with the fingerprint of the key accessible through r exists for the given prefix.
|
||||
func (ks *Keystore) TrustedKeyPrefixWithFingerprintExists(prefix string, r io.ReadSeeker) (bool, error) { |
||||
defer r.Seek(0, io.SeekStart) |
||||
|
||||
entityList, err := openpgp.ReadArmoredKeyRing(r) |
||||
if err != nil { |
||||
return false, err |
||||
} |
||||
if len(entityList) < 1 { |
||||
return false, errors.New("missing opengpg entity") |
||||
} |
||||
pubKey := entityList[0].PrimaryKey |
||||
fileName := fingerprintToFilename(pubKey.Fingerprint) |
||||
|
||||
pathNamesRoot := []string{ |
||||
// example: /etc/rkt/trustedkeys/root.d/8b86de38890ddb7291867b025210bd8888182190
|
||||
path.Join(ks.LocalRootPath, fileName), |
||||
// example: /usr/lib/rkt/trustedkeys/root.d/8b86de38890ddb7291867b025210bd8888182190
|
||||
path.Join(ks.SystemRootPath, fileName), |
||||
} |
||||
return filepath.Walk(ks.Path, func(path string, fi os.FileInfo, err error) error { |
||||
if err != nil && !os.IsNotExist(err) { |
||||
return err |
||||
|
||||
var pathNamesPrefix []string |
||||
if prefix != "" { |
||||
acidentifier, err := types.NewACIdentifier(prefix) |
||||
if err != nil { |
||||
return false, err |
||||
} |
||||
if fi == nil { |
||||
return nil |
||||
pathNamesPrefix = []string{ |
||||
// example: /etc/rkt/trustedkeys/prefix.d/coreos.com/etcd/8b86de38890ddb7291867b025210bd8888182190
|
||||
path.Join(ks.LocalPrefixPath, acidentifier.String(), fileName), |
||||
// example: /usr/lib/rkt/trustedkeys/prefix.d/coreos.com/etcd/8b86de38890ddb7291867b025210bd8888182190
|
||||
path.Join(ks.SystemPrefixPath, acidentifier.String(), fileName), |
||||
} |
||||
} |
||||
|
||||
if fi.IsDir() { |
||||
if namePath == "" || strings.HasPrefix(namePath, path) || fi.Name() == "@" { |
||||
return nil |
||||
} else { |
||||
return filepath.SkipDir |
||||
} |
||||
pathNames := append(pathNamesRoot, pathNamesPrefix...) |
||||
for _, p := range pathNames { |
||||
_, err := os.Stat(p) |
||||
if err == nil { |
||||
return true, nil |
||||
} else if !os.IsNotExist(err) { |
||||
return false, fmt.Errorf("cannot check file %q: %s", p, err) |
||||
} |
||||
} |
||||
|
||||
if prefix, err := pathToACIdentifier(path); err != nil { |
||||
return err |
||||
} else { |
||||
return fn(prefix, path) |
||||
} |
||||
}) |
||||
return false, nil |
||||
} |
||||
|
||||
func walkLoaderFn(kr *Keyring) func(types.ACIdentifier, string) error { |
||||
return func(_ types.ACIdentifier, path string) error { |
||||
return kr.loadFile(path) |
||||
// StoreTrustedKeyPrefix stores the contents of public key r as a prefix trusted key.
|
||||
func (ks *Keystore) StoreTrustedKeyPrefix(prefix string, r io.Reader) (string, error) { |
||||
acidentifier, err := types.NewACIdentifier(prefix) |
||||
if err != nil { |
||||
return "", err |
||||
} |
||||
return storeTrustedKey(path.Join(ks.LocalPrefixPath, acidentifier.String()), r) |
||||
} |
||||
|
||||
func (ks *Keystore) GetAllKeys() (*Keyring, error) { |
||||
kr := &Keyring{} |
||||
if err := ks.walk("", walkLoaderFn(kr)); err != nil { |
||||
return nil, err |
||||
// StoreTrustedKeyRoot stores the contents of public key r as a root trusted key.
|
||||
func (ks *Keystore) StoreTrustedKeyRoot(r io.Reader) (string, error) { |
||||
return storeTrustedKey(ks.LocalRootPath, r) |
||||
} |
||||
|
||||
func storeTrustedKey(dir string, r io.Reader) (string, error) { |
||||
pubkeyBytes, err := ioutil.ReadAll(r) |
||||
if err != nil { |
||||
return "", err |
||||
} |
||||
if err := os.MkdirAll(dir, 0755); err != nil { |
||||
return "", err |
||||
} |
||||
if err := os.Chmod(dir, 0755); err != nil { |
||||
return "", err |
||||
} |
||||
return kr, nil |
||||
entityList, err := openpgp.ReadArmoredKeyRing(bytes.NewReader(pubkeyBytes)) |
||||
if err != nil { |
||||
return "", err |
||||
} |
||||
if len(entityList) < 1 { |
||||
return "", errors.New("missing opengpg entity") |
||||
} |
||||
pubKey := entityList[0].PrimaryKey |
||||
trustedKeyPath := path.Join(dir, fingerprintToFilename(pubKey.Fingerprint)) |
||||
if err := ioutil.WriteFile(trustedKeyPath, pubkeyBytes, 0644); err != nil { |
||||
return "", err |
||||
} |
||||
if err := os.Chmod(trustedKeyPath, 0644); err != nil { |
||||
return "", err |
||||
} |
||||
|
||||
return trustedKeyPath, nil |
||||
} |
||||
|
||||
func (ks *Keystore) GetKeysFor(name types.ACIdentifier) (*Keyring, error) { |
||||
kr := &Keyring{} |
||||
if err := ks.walk(name, walkLoaderFn(kr)); err != nil { |
||||
func entityFromFile(path string) (*openpgp.Entity, error) { |
||||
trustedKey, err := os.Open(path) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
defer trustedKey.Close() |
||||
entityList, err := openpgp.ReadArmoredKeyRing(trustedKey) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
return kr, nil |
||||
if len(entityList) < 1 { |
||||
return nil, errors.New("missing opengpg entity") |
||||
} |
||||
fingerprint := fingerprintToFilename(entityList[0].PrimaryKey.Fingerprint) |
||||
keyFile := filepath.Base(trustedKey.Name()) |
||||
if fingerprint != keyFile { |
||||
return nil, fmt.Errorf("fingerprint mismatch: %q:%q", keyFile, fingerprint) |
||||
} |
||||
return entityList[0], nil |
||||
} |
||||
|
||||
func (ks *Keystore) CheckSignature(name types.ACIdentifier, signed, signature io.Reader) (*openpgp.Entity, error) { |
||||
kr, err := ks.GetKeysFor(name) |
||||
func (ks *Keystore) loadKeyring(prefix string) (openpgp.KeyRing, error) { |
||||
acidentifier, err := types.NewACIdentifier(prefix) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
entities, err := openpgp.CheckArmoredDetachedSignature(kr, signed, signature) |
||||
if err == io.EOF { |
||||
err = fmt.Errorf("no signatures found") |
||||
var keyring openpgp.EntityList |
||||
trustedKeys := make(map[string]*openpgp.Entity) |
||||
|
||||
prefixRoot := strings.Split(acidentifier.String(), "/")[0] |
||||
paths := []struct { |
||||
root string |
||||
fullPath string |
||||
}{ |
||||
{ks.SystemRootPath, ks.SystemRootPath}, |
||||
{ks.LocalRootPath, ks.LocalRootPath}, |
||||
{path.Join(ks.SystemPrefixPath, prefixRoot), path.Join(ks.SystemPrefixPath, acidentifier.String())}, |
||||
{path.Join(ks.LocalPrefixPath, prefixRoot), path.Join(ks.LocalPrefixPath, acidentifier.String())}, |
||||
} |
||||
return entities, err |
||||
for _, p := range paths { |
||||
err := filepath.Walk(p.root, func(path string, info os.FileInfo, err error) error { |
||||
if err != nil && !os.IsNotExist(err) { |
||||
return err |
||||
} |
||||
if info == nil { |
||||
return nil |
||||
} |
||||
if info.IsDir() { |
||||
switch { |
||||
case strings.HasPrefix(p.fullPath, path): |
||||
return nil |
||||
default: |
||||
return filepath.SkipDir |
||||
} |
||||
} |
||||
// Remove trust for default keys.
|
||||
if info.Size() == 0 { |
||||
delete(trustedKeys, info.Name()) |
||||
return nil |
||||
} |
||||
entity, err := entityFromFile(path) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
trustedKeys[fingerprintToFilename(entity.PrimaryKey.Fingerprint)] = entity |
||||
return nil |
||||
}) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
} |
||||
|
||||
for _, v := range trustedKeys { |
||||
keyring = append(keyring, v) |
||||
} |
||||
return keyring, nil |
||||
} |
||||
|
||||
func fingerprintToFilename(fp [20]byte) string { |
||||
return fmt.Sprintf("%x", fp) |
||||
} |
||||
|
||||
// NewTestKeystore creates a new KeyStore backed by a temp directory.
|
||||
// NewTestKeystore returns a KeyStore, the path to the temp directory, and
|
||||
// an error if any.
|
||||
func NewTestKeystore() (*Keystore, string, error) { |
||||
dir, err := ioutil.TempDir("", "keystore-test") |
||||
if err != nil { |
||||
return nil, "", err |
||||
} |
||||
systemDir := filepath.Join(dir, common.DefaultSystemConfigDir) |
||||
localDir := filepath.Join(dir, common.DefaultLocalConfigDir) |
||||
c := NewConfig(systemDir, localDir) |
||||
for _, localPath := range []string{c.LocalRootPath, c.SystemRootPath, c.LocalPrefixPath, c.SystemPrefixPath} { |
||||
if err := os.MkdirAll(localPath, 0755); err != nil { |
||||
return nil, "", err |
||||
} |
||||
if err := os.Chmod(dir, 0755); err != nil { |
||||
return nil, "", err |
||||
} |
||||
|
||||
} |
||||
return New(c), dir, nil |
||||
} |
||||
|
@ -1,273 +1,185 @@
|
||||
// Copyright 2014 The rkt Authors
|
||||
//
|
||||
// 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.
|
||||
|
||||
// The keystore tests require opengpg keys from the keystoretest package (keystoretest.KeyMap).
|
||||
// The opengpg keys are auto generated by running the keygen.go command.
|
||||
// keygen.go should not be run by an automated process. keygen.go is a helper to generate
|
||||
// the keystoretest/keymap.go source file.
|
||||
//
|
||||
// If additional opengpg keys are need for testing, please use the following process:
|
||||
// * add a new key name to keygen.go
|
||||
// * cd keystore/keystoretest
|
||||
// * go run keygen.go
|
||||
// * check in the results
|
||||
|
||||
package keystore |
||||
|
||||
import ( |
||||
"bytes" |
||||
"fmt" |
||||
"io/ioutil" |
||||
"os" |
||||
"path/filepath" |
||||
"reflect" |
||||
"sort" |
||||
"testing" |
||||
|
||||
"github.com/appc/spec/schema/types" |
||||
"git.wegmueller.it/opencloud/opencloud/keystore/keystoretest" |
||||
|
||||
"golang.org/x/crypto/openpgp/errors" |
||||
) |
||||
|
||||
func newStore() *Keystore { |
||||
storePath, err := ioutil.TempDir(".", "test.store.") |
||||
func TestStoreTrustedKey(t *testing.T) { |
||||
ks, ksPath, err := NewTestKeystore() |
||||
if err != nil { |
||||
panic(err) |
||||
t.Errorf("unexpected error %v", err) |
||||
} |
||||
defer os.RemoveAll(ksPath) |
||||
|
||||
return New(storePath) |
||||
} |
||||
armoredPublicKey := keystoretest.KeyMap["example.com"].ArmoredPublicKey |
||||
fingerprint := keystoretest.KeyMap["example.com"].Fingerprint |
||||
|
||||
func testImport(t *testing.T, prefix types.ACIdentifier, subdir string) { |
||||
// Redirect stdout (how to DRY?)
|
||||
origStdout := os.Stdout |
||||
defer func() { os.Stdout = origStdout }() |
||||
if devnull, err := os.Create("/dev/null"); err != nil { |
||||
panic(err) |
||||
} else { |
||||
os.Stdout = devnull |
||||
defer devnull.Close() |
||||
output, err := ks.StoreTrustedKeyPrefix("example.com/foo", bytes.NewBufferString(armoredPublicKey)) |
||||