Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
0fddd1735d | ||
|
f779a3f7f5 | ||
|
7015338aef | ||
|
34aa147ebe | ||
|
4d02e1da8e | ||
|
23d02363ee | ||
|
3c4fe9e260 | ||
|
a594e053f5 | ||
|
f3ba47ac89 | ||
|
7d814396b7 | ||
|
47ca113385 |
@@ -171,6 +171,7 @@ All but the `passwd` and `ssh-authorized-keys` fields will be ignored if the use
|
|||||||
- **no-user-group**: Boolean. Skip default group creation.
|
- **no-user-group**: Boolean. Skip default group creation.
|
||||||
- **ssh-authorized-keys**: List of public SSH keys to authorize for this user
|
- **ssh-authorized-keys**: List of public SSH keys to authorize for this user
|
||||||
- **coreos-ssh-import-github**: Authorize SSH keys from Github user
|
- **coreos-ssh-import-github**: Authorize SSH keys from Github user
|
||||||
|
- **coreos-ssh-import-url**: Authorize SSH keys imported from a url endpoint.
|
||||||
- **system**: Create the user as a system user. No home directory will be created.
|
- **system**: Create the user as a system user. No home directory will be created.
|
||||||
- **no-log-init**: Boolean. Skip initialization of lastlog and faillog databases.
|
- **no-log-init**: Boolean. Skip initialization of lastlog and faillog databases.
|
||||||
|
|
||||||
@@ -187,12 +188,12 @@ The following fields are not yet implemented:
|
|||||||
|
|
||||||
users:
|
users:
|
||||||
- name: elroy
|
- name: elroy
|
||||||
passwd: $6$5s2u6/jR$un0AvWnqilcgaNB3Mkxd5yYv6mTlWfOoCYHZmfi3LDKVltj.E8XNKEcwWm...
|
passwd: $6$5s2u6/jR$un0AvWnqilcgaNB3Mkxd5yYv6mTlWfOoCYHZmfi3LDKVltj.E8XNKEcwWm...
|
||||||
groups:
|
groups:
|
||||||
- staff
|
- staff
|
||||||
- docker
|
- docker
|
||||||
ssh-authorized-keys:
|
ssh-authorized-keys:
|
||||||
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0g+ZTxC7weoIJLUafOgrm+h...
|
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0g+ZTxC7weoIJLUafOgrm+h...
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Generating a password hash
|
#### Generating a password hash
|
||||||
@@ -215,6 +216,41 @@ perl -e 'print crypt("password","\$6\$SALT\$") . "\n"'
|
|||||||
|
|
||||||
Using a higher number of rounds will help create more secure passwords, but given enough time, password hashes can be reversed. On most RPM based distributions there is a tool called mkpasswd available in the `expect` package, but this does not handle "rounds" nor advanced hashing algorithms.
|
Using a higher number of rounds will help create more secure passwords, but given enough time, password hashes can be reversed. On most RPM based distributions there is a tool called mkpasswd available in the `expect` package, but this does not handle "rounds" nor advanced hashing algorithms.
|
||||||
|
|
||||||
|
#### Retrieving ssh authorized keys from a GitHub user
|
||||||
|
|
||||||
|
Using the field `coreos-ssh-import-github` you can make coreos-cloudinit to add the public ssh keys from a GitHub user as authorized keys to a server.
|
||||||
|
|
||||||
|
```
|
||||||
|
#cloud-config
|
||||||
|
|
||||||
|
users:
|
||||||
|
- name: elroy
|
||||||
|
coreos-ssh-import-github: elroy
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Retrieving ssh authorized keys from an http endpoint
|
||||||
|
|
||||||
|
coreos-cloudinit can also pull public SSH keys from any http endpoint that matches [GitHub's API response format](https://developer.github.com/v3/users/keys/#list-public-keys-for-a-user).
|
||||||
|
For example, if you have an installation of GitHub Enterprise, you can provide a complete url with an authentication token:
|
||||||
|
|
||||||
|
```
|
||||||
|
#cloud-config
|
||||||
|
|
||||||
|
users:
|
||||||
|
- name: elroy
|
||||||
|
coreos-ssh-import-url: https://token:<OAUTH-TOKEN>@github-enterprise.example.com/users/elroy/keys
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also provide any url which response matches that json format for public keys:
|
||||||
|
|
||||||
|
```
|
||||||
|
#cloud-config
|
||||||
|
|
||||||
|
users:
|
||||||
|
- name: elroy
|
||||||
|
coreos-ssh-import-url: https://example.com/public-keys
|
||||||
|
```
|
||||||
|
|
||||||
### write_files
|
### write_files
|
||||||
|
|
||||||
Inject an arbitrary set of files to the local filesystem.
|
Inject an arbitrary set of files to the local filesystem.
|
||||||
@@ -224,3 +260,16 @@ Provide a list of objects with the following attributes:
|
|||||||
- **content**: Data to write at the provided `path`
|
- **content**: Data to write at the provided `path`
|
||||||
- **permissions**: String representing file permissions in octal notation (i.e. '0644')
|
- **permissions**: String representing file permissions in octal notation (i.e. '0644')
|
||||||
- **owner**: User and group that should own the file written to disk. This is equivalent to the `<user>:<group>` argument to `chown <user>:<group> <path>`.
|
- **owner**: User and group that should own the file written to disk. This is equivalent to the `<user>:<group>` argument to `chown <user>:<group> <path>`.
|
||||||
|
|
||||||
|
### manage_etc_hosts
|
||||||
|
|
||||||
|
Have coreos-cloudinit manage your /etc/hosts file for local name resolution.
|
||||||
|
The only supported value is "localhost" which will cause your system's hostname
|
||||||
|
to resolve to "127.0.0.1". This is helpful when the host does not have DNS
|
||||||
|
infrastructure in place to resolve its own hostname, for example, when using Vagrant.
|
||||||
|
|
||||||
|
```
|
||||||
|
#cloud-config
|
||||||
|
|
||||||
|
manage_etc_hosts: localhost
|
||||||
|
```
|
||||||
|
@@ -12,7 +12,7 @@ import (
|
|||||||
"github.com/coreos/coreos-cloudinit/system"
|
"github.com/coreos/coreos-cloudinit/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
const version = "0.3.2"
|
const version = "0.4.0"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var printVersion bool
|
var printVersion bool
|
||||||
|
@@ -17,9 +17,10 @@ type CloudConfig struct {
|
|||||||
Units []system.Unit
|
Units []system.Unit
|
||||||
OEM OEMRelease
|
OEM OEMRelease
|
||||||
}
|
}
|
||||||
WriteFiles []system.File `yaml:"write_files"`
|
WriteFiles []system.File `yaml:"write_files"`
|
||||||
Hostname string
|
Hostname string
|
||||||
Users []system.User
|
Users []system.User
|
||||||
|
ManageEtcHosts string `yaml:"manage_etc_hosts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCloudConfig(contents string) (*CloudConfig, error) {
|
func NewCloudConfig(contents string) (*CloudConfig, error) {
|
||||||
@@ -91,6 +92,12 @@ func Apply(cfg CloudConfig, env *Environment) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if user.SSHImportURL != "" {
|
||||||
|
log.Printf("Authorizing SSH keys for CoreOS user '%s' from '%s'", user.Name, user.SSHImportURL)
|
||||||
|
if err := SSHImportKeysFromURL(user.Name, user.SSHImportURL); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +155,7 @@ func Apply(cfg CloudConfig, env *Environment) error {
|
|||||||
commands["systemd-networkd.service"] = "restart"
|
commands["systemd-networkd.service"] = "restart"
|
||||||
} else {
|
} else {
|
||||||
if unit.Command != "" {
|
if unit.Command != "" {
|
||||||
commands[unit.Name] = unit.Command
|
commands[unit.Name] = unit.Command
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,5 +170,15 @@ func Apply(cfg CloudConfig, env *Environment) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.ManageEtcHosts != "" {
|
||||||
|
|
||||||
|
if err := WriteEtcHosts(cfg.ManageEtcHosts, env.Root()); err != nil {
|
||||||
|
log.Fatalf("Failed to write /etc/hosts to filesystem: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Wrote /etc/hosts file to filesystem")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@@ -1,52 +1,18 @@
|
|||||||
package initialize
|
package initialize
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/coreos/coreos-cloudinit/system"
|
"github.com/coreos/coreos-cloudinit/system"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GithubUserKey struct {
|
|
||||||
Id int `json:"id"`
|
|
||||||
Key string `json:"key"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchGithubKeys(github_url string) ([]string, error) {
|
|
||||||
res, err := http.Get(github_url)
|
|
||||||
defer res.Body.Close()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
body, err := ioutil.ReadAll(res.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var data []GithubUserKey
|
|
||||||
err = json.Unmarshal(body, &data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
keys := make([]string, 0)
|
|
||||||
for _, key := range data {
|
|
||||||
keys = append(keys, key.Key)
|
|
||||||
}
|
|
||||||
return keys, err
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func SSHImportGithubUser(system_user string, github_user string) error {
|
func SSHImportGithubUser(system_user string, github_user string) error {
|
||||||
url := fmt.Sprintf("https://api.github.com/users/%s/keys", github_user)
|
url := fmt.Sprintf("https://api.github.com/users/%s/keys", github_user)
|
||||||
keys, err := fetchGithubKeys(url)
|
keys, err := fetchUserKeys(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
key_name := fmt.Sprintf("github-%s", github_user)
|
key_name := fmt.Sprintf("github-%s", github_user)
|
||||||
err = system.AuthorizeSSHKeys(system_user, key_name, keys)
|
return system.AuthorizeSSHKeys(system_user, key_name, keys)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
@@ -1,48 +1,9 @@
|
|||||||
package initialize
|
package initialize
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCloudConfigUsersGithubMarshal(t *testing.T) {
|
|
||||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
gh_res := `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"id": 67057,
|
|
||||||
"key": "ssh-dss AAAAB3NzaC1kc3MAAACBAIHAu822ggSkIHrJYvhmBceOSVjuflfQm8RbMMDNVe9relQfuPbN+nxGGTCKzPLebeOcX+Wwi77TPXWwK3BZMglfXxhABlFPsuMb63Tqp94pBYsJdx/iFj9iGo6pKoM1k8ubOcqsUnq+BR9895zRbE7MjdwkGo67+QhCEwvkwAnNAAAAFQCuddVqXLCubzqnWmeHLQE+2GFfHwAAAIBnlXW5h15ndVuwi0htF4oodVSB1KwnTWcuBK+aE1zRs76yvRb0Ws+oifumThDwB/Tec6FQuAfRKfy6piChZqsu5KvL98I+2t5yyi1td+kMvdTnVL2lW44etDKseOcozmknCOmh4Dqvhl/2MwrDAhlPaN08EEq9h3w3mXtNLWH64QAAAIBAzDOKr17llngaKIdDXh+LtXKh87+zfjlTA36/9r2uF2kYE5uApDtu9sPCkt7+YBQt7R8prADPckwAiXwVdk0xijIOpLDBmoydQJJRQ+zTMxvpQmUr/1kUOv0zb+lB657CgvN0vVTmP2swPeMvgntt3C4vw7Ab+O+MS9peOAJbbQ=="
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3340477,
|
|
||||||
"key": "ssh-dss AAAAB3NzaC1kc3MAAACBANxpzIbTzKTeBRaOIdUxwwGwvDasTfU/PonhbNIuhYjc+xFGvBRTumox2F+luVAKKs4WdvA4nJXaY1OFi6DZftk5Bp4E2JaSzp8ulAzHsMexDdv6LGHGEJj/qdHAL1vHk2K89PpwRFSRZI8XRBLjvkr4ZgBKLG5ZILXPJEPP2j3lAAAAFQCtxoTnV8wy0c4grcGrQ+1sCsD7WQAAAIAqZsW2GviMe1RQrbZT0xAZmI64XRPrnLsoLxycHWlS7r6uUln2c6Ae2MB/YF0d4Kd1XZii9GHj7rrypqEo7MW8uSabhu70nmu1J8m2O3Dsr+4oJLeat9vwPsJV92IKO0jQwjKnAOHOiB9JKGeCw+NfXfogbti9/q38Q6XcS+SI5wAAAIEA1803Y2h+tOOpZXAsNIwl9mRfExWzLQ3L7knwJdznQu/6SW1H/1oyoYLebuk187Qj2UFI5qQ6AZNc49DvohWx0Cg6ABcyubNyoaCjZKWIdxVnItHWNbLe//+tyTu0I2eQwJOORsEPK5gMpf599C7wXQ//DzZOWbTWiHEX52gCTmk="
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 5224438,
|
|
||||||
"key": "ssh-dss AAAAB3NzaC1kc3MAAACBAPKRWdKhzGZuLAJL6M1eM51hWViMqNBC2C6lm2OqGRYLuIf1GJ391widUuSf4wQqnkR22Q9PCmAZ19XCf11wBRMnuw9I/Z3Bt5bXfc+dzFBCmHYGJ6wNSv++H9jxyMb+usmsenWOFZGNO2jN0wrJ4ay8Yt0bwtRU+VCXpuRLszMzAAAAFQDZUIuPjcfK5HLgnwZ/J3lvtvlUjQAAAIEApIkAwLuCQV5j3U6DmI/Y6oELqSUR2purFm8jo8jePFfe1t+ghikgD254/JXlhDCVgY0NLXcak+coJfGCTT23quJ7I5xdpTn/OZO2Q6Woum/bijFC/UWwQbLz0R2nU3DoHv5v6XHQZxuIG4Fsxa91S+vWjZFtI7RuYlBCZA//ANMAAACBAJO0FojzkX6IeaWLqrgu9GTkFwGFazZ+LPH5JOWPoPn1hQKuR32Uf6qNcBZcIjY7SF0P7HF5rLQd6zKZzHqqQQ92MV555NEwjsnJglYU8CaaZsfYooaGPgA1YN7RhTSAuDmUW5Hyfj5BH4NTtrzrvJxIhDoQLf31Fasjw00r4R0O"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
`
|
|
||||||
fmt.Fprintln(w, gh_res)
|
|
||||||
}))
|
|
||||||
defer ts.Close()
|
|
||||||
|
|
||||||
keys, err := fetchGithubKeys(ts.URL)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Encountered unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
expected := "ssh-dss AAAAB3NzaC1kc3MAAACBAIHAu822ggSkIHrJYvhmBceOSVjuflfQm8RbMMDNVe9relQfuPbN+nxGGTCKzPLebeOcX+Wwi77TPXWwK3BZMglfXxhABlFPsuMb63Tqp94pBYsJdx/iFj9iGo6pKoM1k8ubOcqsUnq+BR9895zRbE7MjdwkGo67+QhCEwvkwAnNAAAAFQCuddVqXLCubzqnWmeHLQE+2GFfHwAAAIBnlXW5h15ndVuwi0htF4oodVSB1KwnTWcuBK+aE1zRs76yvRb0Ws+oifumThDwB/Tec6FQuAfRKfy6piChZqsu5KvL98I+2t5yyi1td+kMvdTnVL2lW44etDKseOcozmknCOmh4Dqvhl/2MwrDAhlPaN08EEq9h3w3mXtNLWH64QAAAIBAzDOKr17llngaKIdDXh+LtXKh87+zfjlTA36/9r2uF2kYE5uApDtu9sPCkt7+YBQt7R8prADPckwAiXwVdk0xijIOpLDBmoydQJJRQ+zTMxvpQmUr/1kUOv0zb+lB657CgvN0vVTmP2swPeMvgntt3C4vw7Ab+O+MS9peOAJbbQ=="
|
|
||||||
if keys[0] != expected {
|
|
||||||
t.Fatalf("expected %s, got %s", expected, keys[0])
|
|
||||||
}
|
|
||||||
expected = "ssh-dss AAAAB3NzaC1kc3MAAACBAPKRWdKhzGZuLAJL6M1eM51hWViMqNBC2C6lm2OqGRYLuIf1GJ391widUuSf4wQqnkR22Q9PCmAZ19XCf11wBRMnuw9I/Z3Bt5bXfc+dzFBCmHYGJ6wNSv++H9jxyMb+usmsenWOFZGNO2jN0wrJ4ay8Yt0bwtRU+VCXpuRLszMzAAAAFQDZUIuPjcfK5HLgnwZ/J3lvtvlUjQAAAIEApIkAwLuCQV5j3U6DmI/Y6oELqSUR2purFm8jo8jePFfe1t+ghikgD254/JXlhDCVgY0NLXcak+coJfGCTT23quJ7I5xdpTn/OZO2Q6Woum/bijFC/UWwQbLz0R2nU3DoHv5v6XHQZxuIG4Fsxa91S+vWjZFtI7RuYlBCZA//ANMAAACBAJO0FojzkX6IeaWLqrgu9GTkFwGFazZ+LPH5JOWPoPn1hQKuR32Uf6qNcBZcIjY7SF0P7HF5rLQd6zKZzHqqQQ92MV555NEwjsnJglYU8CaaZsfYooaGPgA1YN7RhTSAuDmUW5Hyfj5BH4NTtrzrvJxIhDoQLf31Fasjw00r4R0O"
|
|
||||||
if keys[2] != expected {
|
|
||||||
t.Fatalf("expected %s, got %s", expected, keys[2])
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
func TestCloudConfigUsersGithubUser(t *testing.T) {
|
func TestCloudConfigUsersGithubUser(t *testing.T) {
|
||||||
|
|
||||||
contents := `
|
contents := `
|
||||||
|
44
initialize/manage_etc_hosts.go
Normal file
44
initialize/manage_etc_hosts.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package initialize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/coreos/coreos-cloudinit/system"
|
||||||
|
)
|
||||||
|
|
||||||
|
const DefaultIpv4Address = "127.0.0.1"
|
||||||
|
|
||||||
|
func generateEtcHosts(option string) (out string, err error) {
|
||||||
|
if option != "localhost" {
|
||||||
|
return "", errors.New("Invalid option to manage_etc_hosts")
|
||||||
|
}
|
||||||
|
|
||||||
|
// use the operating system hostname
|
||||||
|
hostname, err := os.Hostname()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s %s", DefaultIpv4Address, hostname), nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write an /etc/hosts file
|
||||||
|
func WriteEtcHosts(option string, root string) error {
|
||||||
|
|
||||||
|
etcHosts, err := generateEtcHosts(option)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
file := system.File{
|
||||||
|
Path: path.Join(root, "etc", "hosts"),
|
||||||
|
RawFilePermissions: "0644",
|
||||||
|
Content: etcHosts,
|
||||||
|
}
|
||||||
|
|
||||||
|
return system.WriteFile(&file)
|
||||||
|
}
|
76
initialize/manage_etc_hosts_test.go
Normal file
76
initialize/manage_etc_hosts_test.go
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package initialize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCloudConfigManageEtcHosts(t *testing.T) {
|
||||||
|
contents := `
|
||||||
|
manage_etc_hosts: localhost
|
||||||
|
`
|
||||||
|
cfg, err := NewCloudConfig(contents)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Encountered unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
manageEtcHosts := cfg.ManageEtcHosts
|
||||||
|
|
||||||
|
if manageEtcHosts != "localhost" {
|
||||||
|
t.Errorf("ManageEtcHosts value is %q, expected 'localhost'", manageEtcHosts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestManageEtcHostsInvalidValue(t *testing.T) {
|
||||||
|
dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to create tempdir: %v", err)
|
||||||
|
}
|
||||||
|
defer rmdir(dir)
|
||||||
|
|
||||||
|
if err := WriteEtcHosts("invalid", dir); err == nil {
|
||||||
|
t.Fatalf("WriteEtcHosts succeeded with invalid value: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEtcHostsWrittenToDisk(t *testing.T) {
|
||||||
|
dir, err := ioutil.TempDir(os.TempDir(), "coreos-cloudinit-")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to create tempdir: %v", err)
|
||||||
|
}
|
||||||
|
defer rmdir(dir)
|
||||||
|
|
||||||
|
if err := WriteEtcHosts("localhost", dir); err != nil {
|
||||||
|
t.Fatalf("WriteEtcHosts failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fullPath := path.Join(dir, "etc", "hosts")
|
||||||
|
|
||||||
|
fi, err := os.Stat(fullPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to stat file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fi.Mode() != os.FileMode(0644) {
|
||||||
|
t.Errorf("File has incorrect mode: %v", fi.Mode())
|
||||||
|
}
|
||||||
|
|
||||||
|
contents, err := ioutil.ReadFile(fullPath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to read expected file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hostname, err := os.Hostname()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unable to read OS hostname: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expect := fmt.Sprintf("%s %s", DefaultIpv4Address, hostname)
|
||||||
|
|
||||||
|
if string(contents) != expect {
|
||||||
|
t.Fatalf("File has incorrect contents")
|
||||||
|
}
|
||||||
|
}
|
@@ -18,9 +18,9 @@ type OEMRelease struct {
|
|||||||
|
|
||||||
func (oem *OEMRelease) String() string {
|
func (oem *OEMRelease) String() string {
|
||||||
fields := []string{
|
fields := []string{
|
||||||
fmt.Sprintf("ID=%q", oem.ID),
|
fmt.Sprintf("ID=%s", oem.ID),
|
||||||
|
fmt.Sprintf("VERSION_ID=%s", oem.VersionID),
|
||||||
fmt.Sprintf("NAME=%q", oem.Name),
|
fmt.Sprintf("NAME=%q", oem.Name),
|
||||||
fmt.Sprintf("VERSION_ID=%q", oem.VersionID),
|
|
||||||
fmt.Sprintf("HOME_URL=%q", oem.HomeURL),
|
fmt.Sprintf("HOME_URL=%q", oem.HomeURL),
|
||||||
fmt.Sprintf("BUG_REPORT_URL=%q", oem.BugReportURL),
|
fmt.Sprintf("BUG_REPORT_URL=%q", oem.BugReportURL),
|
||||||
}
|
}
|
||||||
|
@@ -42,9 +42,9 @@ func TestOEMReleaseWrittenToDisk(t *testing.T) {
|
|||||||
t.Fatalf("Unable to read expected file: %v", err)
|
t.Fatalf("Unable to read expected file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
expect := `ID="rackspace"
|
expect := `ID=rackspace
|
||||||
|
VERSION_ID=168.0.0
|
||||||
NAME="Rackspace Cloud Servers"
|
NAME="Rackspace Cloud Servers"
|
||||||
VERSION_ID="168.0.0"
|
|
||||||
HOME_URL="https://www.rackspace.com/cloud/servers/"
|
HOME_URL="https://www.rackspace.com/cloud/servers/"
|
||||||
BUG_REPORT_URL="https://github.com/coreos/coreos-overlay"
|
BUG_REPORT_URL="https://github.com/coreos/coreos-overlay"
|
||||||
`
|
`
|
||||||
|
47
initialize/ssh_keys.go
Normal file
47
initialize/ssh_keys.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package initialize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/coreos/coreos-cloudinit/system"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserKey struct {
|
||||||
|
ID int `json:"id,omitempty"`
|
||||||
|
Key string `json:"key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func SSHImportKeysFromURL(system_user string, url string) error {
|
||||||
|
keys, err := fetchUserKeys(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
key_name := fmt.Sprintf("coreos-cloudinit-%s", system_user)
|
||||||
|
return system.AuthorizeSSHKeys(system_user, key_name, keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchUserKeys(url string) ([]string, error) {
|
||||||
|
res, err := http.Get(url)
|
||||||
|
defer res.Body.Close()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
body, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var data []UserKey
|
||||||
|
err = json.Unmarshal(body, &data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
keys := make([]string, 0)
|
||||||
|
for _, key := range data {
|
||||||
|
keys = append(keys, key.Key)
|
||||||
|
}
|
||||||
|
return keys, err
|
||||||
|
}
|
69
initialize/ssh_keys_test.go
Normal file
69
initialize/ssh_keys_test.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package initialize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCloudConfigUsersUrlMarshal(t *testing.T) {
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
gh_res := `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"key": "ssh-dss AAAAB3NzaC1kc3MAAACBAIHAu822ggSkIHrJYvhmBceOSVjuflfQm8RbMMDNVe9relQfuPbN+nxGGTCKzPLebeOcX+Wwi77TPXWwK3BZMglfXxhABlFPsuMb63Tqp94pBYsJdx/iFj9iGo6pKoM1k8ubOcqsUnq+BR9895zRbE7MjdwkGo67+QhCEwvkwAnNAAAAFQCuddVqXLCubzqnWmeHLQE+2GFfHwAAAIBnlXW5h15ndVuwi0htF4oodVSB1KwnTWcuBK+aE1zRs76yvRb0Ws+oifumThDwB/Tec6FQuAfRKfy6piChZqsu5KvL98I+2t5yyi1td+kMvdTnVL2lW44etDKseOcozmknCOmh4Dqvhl/2MwrDAhlPaN08EEq9h3w3mXtNLWH64QAAAIBAzDOKr17llngaKIdDXh+LtXKh87+zfjlTA36/9r2uF2kYE5uApDtu9sPCkt7+YBQt7R8prADPckwAiXwVdk0xijIOpLDBmoydQJJRQ+zTMxvpQmUr/1kUOv0zb+lB657CgvN0vVTmP2swPeMvgntt3C4vw7Ab+O+MS9peOAJbbQ=="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "ssh-dss AAAAB3NzaC1kc3MAAACBANxpzIbTzKTeBRaOIdUxwwGwvDasTfU/PonhbNIuhYjc+xFGvBRTumox2F+luVAKKs4WdvA4nJXaY1OFi6DZftk5Bp4E2JaSzp8ulAzHsMexDdv6LGHGEJj/qdHAL1vHk2K89PpwRFSRZI8XRBLjvkr4ZgBKLG5ZILXPJEPP2j3lAAAAFQCtxoTnV8wy0c4grcGrQ+1sCsD7WQAAAIAqZsW2GviMe1RQrbZT0xAZmI64XRPrnLsoLxycHWlS7r6uUln2c6Ae2MB/YF0d4Kd1XZii9GHj7rrypqEo7MW8uSabhu70nmu1J8m2O3Dsr+4oJLeat9vwPsJV92IKO0jQwjKnAOHOiB9JKGeCw+NfXfogbti9/q38Q6XcS+SI5wAAAIEA1803Y2h+tOOpZXAsNIwl9mRfExWzLQ3L7knwJdznQu/6SW1H/1oyoYLebuk187Qj2UFI5qQ6AZNc49DvohWx0Cg6ABcyubNyoaCjZKWIdxVnItHWNbLe//+tyTu0I2eQwJOORsEPK5gMpf599C7wXQ//DzZOWbTWiHEX52gCTmk="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5224438,
|
||||||
|
"key": "ssh-dss AAAAB3NzaC1kc3MAAACBAPKRWdKhzGZuLAJL6M1eM51hWViMqNBC2C6lm2OqGRYLuIf1GJ391widUuSf4wQqnkR22Q9PCmAZ19XCf11wBRMnuw9I/Z3Bt5bXfc+dzFBCmHYGJ6wNSv++H9jxyMb+usmsenWOFZGNO2jN0wrJ4ay8Yt0bwtRU+VCXpuRLszMzAAAAFQDZUIuPjcfK5HLgnwZ/J3lvtvlUjQAAAIEApIkAwLuCQV5j3U6DmI/Y6oELqSUR2purFm8jo8jePFfe1t+ghikgD254/JXlhDCVgY0NLXcak+coJfGCTT23quJ7I5xdpTn/OZO2Q6Woum/bijFC/UWwQbLz0R2nU3DoHv5v6XHQZxuIG4Fsxa91S+vWjZFtI7RuYlBCZA//ANMAAACBAJO0FojzkX6IeaWLqrgu9GTkFwGFazZ+LPH5JOWPoPn1hQKuR32Uf6qNcBZcIjY7SF0P7HF5rLQd6zKZzHqqQQ92MV555NEwjsnJglYU8CaaZsfYooaGPgA1YN7RhTSAuDmUW5Hyfj5BH4NTtrzrvJxIhDoQLf31Fasjw00r4R0O"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
`
|
||||||
|
fmt.Fprintln(w, gh_res)
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
keys, err := fetchUserKeys(ts.URL)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Encountered unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
expected := "ssh-dss AAAAB3NzaC1kc3MAAACBAIHAu822ggSkIHrJYvhmBceOSVjuflfQm8RbMMDNVe9relQfuPbN+nxGGTCKzPLebeOcX+Wwi77TPXWwK3BZMglfXxhABlFPsuMb63Tqp94pBYsJdx/iFj9iGo6pKoM1k8ubOcqsUnq+BR9895zRbE7MjdwkGo67+QhCEwvkwAnNAAAAFQCuddVqXLCubzqnWmeHLQE+2GFfHwAAAIBnlXW5h15ndVuwi0htF4oodVSB1KwnTWcuBK+aE1zRs76yvRb0Ws+oifumThDwB/Tec6FQuAfRKfy6piChZqsu5KvL98I+2t5yyi1td+kMvdTnVL2lW44etDKseOcozmknCOmh4Dqvhl/2MwrDAhlPaN08EEq9h3w3mXtNLWH64QAAAIBAzDOKr17llngaKIdDXh+LtXKh87+zfjlTA36/9r2uF2kYE5uApDtu9sPCkt7+YBQt7R8prADPckwAiXwVdk0xijIOpLDBmoydQJJRQ+zTMxvpQmUr/1kUOv0zb+lB657CgvN0vVTmP2swPeMvgntt3C4vw7Ab+O+MS9peOAJbbQ=="
|
||||||
|
if keys[0] != expected {
|
||||||
|
t.Fatalf("expected %s, got %s", expected, keys[0])
|
||||||
|
}
|
||||||
|
expected = "ssh-dss AAAAB3NzaC1kc3MAAACBAPKRWdKhzGZuLAJL6M1eM51hWViMqNBC2C6lm2OqGRYLuIf1GJ391widUuSf4wQqnkR22Q9PCmAZ19XCf11wBRMnuw9I/Z3Bt5bXfc+dzFBCmHYGJ6wNSv++H9jxyMb+usmsenWOFZGNO2jN0wrJ4ay8Yt0bwtRU+VCXpuRLszMzAAAAFQDZUIuPjcfK5HLgnwZ/J3lvtvlUjQAAAIEApIkAwLuCQV5j3U6DmI/Y6oELqSUR2purFm8jo8jePFfe1t+ghikgD254/JXlhDCVgY0NLXcak+coJfGCTT23quJ7I5xdpTn/OZO2Q6Woum/bijFC/UWwQbLz0R2nU3DoHv5v6XHQZxuIG4Fsxa91S+vWjZFtI7RuYlBCZA//ANMAAACBAJO0FojzkX6IeaWLqrgu9GTkFwGFazZ+LPH5JOWPoPn1hQKuR32Uf6qNcBZcIjY7SF0P7HF5rLQd6zKZzHqqQQ92MV555NEwjsnJglYU8CaaZsfYooaGPgA1YN7RhTSAuDmUW5Hyfj5BH4NTtrzrvJxIhDoQLf31Fasjw00r4R0O"
|
||||||
|
if keys[2] != expected {
|
||||||
|
t.Fatalf("expected %s, got %s", expected, keys[2])
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
func TestCloudConfigUsersSSHImportURL(t *testing.T) {
|
||||||
|
|
||||||
|
contents := `
|
||||||
|
users:
|
||||||
|
- name: elroy
|
||||||
|
coreos-ssh-import-url: https://token:x-auth-token@github.enterprise.com/api/v3/polvi/keys
|
||||||
|
`
|
||||||
|
cfg, err := NewCloudConfig(contents)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Encountered unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cfg.Users) != 1 {
|
||||||
|
t.Fatalf("Parsed %d users, expected 1", cfg.Users)
|
||||||
|
}
|
||||||
|
|
||||||
|
user := cfg.Users[0]
|
||||||
|
|
||||||
|
if user.Name != "elroy" {
|
||||||
|
t.Errorf("User name is %q, expected 'elroy'", user.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.SSHImportURL != "https://token:x-auth-token@github.enterprise.com/api/v3/polvi/keys" {
|
||||||
|
t.Errorf("ssh import url is %q, expected 'https://token:x-auth-token@github.enterprise.com/api/v3/polvi/keys'", user.SSHImportURL)
|
||||||
|
}
|
||||||
|
}
|
@@ -13,6 +13,7 @@ type User struct {
|
|||||||
PasswordHash string `yaml:"passwd"`
|
PasswordHash string `yaml:"passwd"`
|
||||||
SSHAuthorizedKeys []string `yaml:"ssh-authorized-keys"`
|
SSHAuthorizedKeys []string `yaml:"ssh-authorized-keys"`
|
||||||
SSHImportGithubUser string `yaml:"coreos-ssh-import-github"`
|
SSHImportGithubUser string `yaml:"coreos-ssh-import-github"`
|
||||||
|
SSHImportURL string `yaml:"coreos-ssh-import-url"`
|
||||||
GECOS string `yaml:"gecos"`
|
GECOS string `yaml:"gecos"`
|
||||||
Homedir string `yaml:"homedir"`
|
Homedir string `yaml:"homedir"`
|
||||||
NoCreateHome bool `yaml:"no-create-home"`
|
NoCreateHome bool `yaml:"no-create-home"`
|
||||||
|
Reference in New Issue
Block a user