summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorStuart Clark <stuart.clark@Jahingo.com>2017-08-08 02:20:56 +0100
committerDave Henderson <dhenderson@gmail.com>2017-08-07 21:20:56 -0400
commitd2cf55b83fe71d41c3a09b35b280d9a48b24088d (patch)
treecc152e6ed7d4f2346d179bb867a683e47a3d9852 /test
parentcbc2af15fccdfc43ae6dfc2ae2d88248df039ddb (diff)
Vault AWS EC2 auth (#190)
Diffstat (limited to 'test')
-rw-r--r--test/integration/.gitignore2
-rw-r--r--test/integration/Dockerfile4
-rw-r--r--test/integration/awssvc/main.go190
-rw-r--r--test/integration/datasources_vault.bats21
-rw-r--r--test/integration/helper.bash18
-rw-r--r--test/integration/metasvc/main.go142
6 files changed, 375 insertions, 2 deletions
diff --git a/test/integration/.gitignore b/test/integration/.gitignore
index 9c2b49ab..62c29879 100644
--- a/test/integration/.gitignore
+++ b/test/integration/.gitignore
@@ -1,2 +1,4 @@
gomplate
mirror
+meta
+aws
diff --git a/test/integration/Dockerfile b/test/integration/Dockerfile
index e8cbc2ce..894a04a7 100644
--- a/test/integration/Dockerfile
+++ b/test/integration/Dockerfile
@@ -1,6 +1,6 @@
FROM alpine:edge
-ENV VAULT_VER 0.7.0
+ENV VAULT_VER 0.7.3
ENV CONSUL_VER 0.9.0
RUN apk add --no-cache \
curl \
@@ -21,6 +21,8 @@ RUN mkdir /lib64 \
COPY gomplate /bin/gomplate
COPY mirror /bin/mirror
+COPY meta /bin/meta
+COPY aws /bin/aws
COPY *.sh /tests/
COPY *.bash /tests/
COPY *.bats /tests/
diff --git a/test/integration/awssvc/main.go b/test/integration/awssvc/main.go
new file mode 100644
index 00000000..0a3f8f05
--- /dev/null
+++ b/test/integration/awssvc/main.go
@@ -0,0 +1,190 @@
+package main
+
+import (
+ "encoding/json"
+ "flag"
+ "log"
+ "net"
+ "net/http"
+)
+
+// Req -
+type Req struct {
+ Headers http.Header `json:"headers"`
+}
+
+var port string
+
+func main() {
+ flag.StringVar(&port, "p", "8082", "Port to listen to")
+ flag.Parse()
+
+ l, err := net.Listen("tcp", ":"+port)
+ if err != nil {
+ log.Fatal(err)
+ }
+ // defer l.Close()
+ http.HandleFunc("/", rootHandler)
+
+ http.HandleFunc("/sts/", stsHandler)
+ http.HandleFunc("/ec2/", ec2Handler)
+ http.HandleFunc("/quit", quitHandler(l))
+
+ http.Serve(l, nil)
+}
+
+func rootHandler(w http.ResponseWriter, r *http.Request) {
+ req := Req{r.Header}
+ b, err := json.Marshal(req)
+ if err != nil {
+ log.Println(err)
+ w.WriteHeader(http.StatusBadRequest)
+ }
+ w.Header().Set("Content-Type", "application/json")
+ w.Write(b)
+}
+
+func stsHandler(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/xml")
+ w.Write([]byte(`<GetCallerIdentityResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
+ <GetCallerIdentityResult>
+ <Arn>arn:aws:iam::1:user/Test</Arn>
+ <UserId>AKIAI44QH8DHBEXAMPLE</UserId>
+ <Account>1</Account>
+ </GetCallerIdentityResult>
+ <ResponseMetadata>
+ <RequestId>01234567-89ab-cdef-0123-456789abcdef</RequestId>
+ </ResponseMetadata>
+</GetCallerIdentityResponse>`))
+}
+
+func ec2Handler(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/xml")
+ w.Write([]byte(`<DescribeInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
+ <requestId>8f7724cf-496f-496e-8fe3-example</requestId>
+ <reservationSet>
+ <item>
+ <reservationId>r-1234567890abcdef0</reservationId>
+ <ownerId>123456789012</ownerId>
+ <groupSet/>
+ <instancesSet>
+ <item>
+ <instanceId>i-00000000000000000</instanceId>
+ <imageId>ami-00000000</imageId>
+ <instanceState>
+ <code>16</code>
+ <name>running</name>
+ </instanceState>
+ <privateDnsName>ip-192-168-1-88.eu-west-1.compute.internal</privateDnsName>
+ <dnsName>ec2-54-194-252-215.eu-west-1.compute.amazonaws.com</dnsName>
+ <reason/>
+ <keyName>my_keypair</keyName>
+ <amiLaunchIndex>0</amiLaunchIndex>
+ <productCodes/>
+ <instanceType>t2.micro</instanceType>
+ <launchTime>2015-12-22T10:44:05.000Z</launchTime>
+ <placement>
+ <availabilityZone>eu-west-1c</availabilityZone>
+ <groupName/>
+ <tenancy>default</tenancy>
+ </placement>
+ <monitoring>
+ <state>disabled</state>
+ </monitoring>
+ <subnetId>subnet-56f5f633</subnetId>
+ <vpcId>vpc-11112222</vpcId>
+ <privateIpAddress>192.168.1.88</privateIpAddress>
+ <ipAddress>54.194.252.215</ipAddress>
+ <sourceDestCheck>true</sourceDestCheck>
+ <groupSet>
+ <item>
+ <groupId>sg-e4076980</groupId>
+ <groupName>SecurityGroup1</groupName>
+ </item>
+ </groupSet>
+ <architecture>x86_64</architecture>
+ <rootDeviceType>ebs</rootDeviceType>
+ <rootDeviceName>/dev/xvda</rootDeviceName>
+ <blockDeviceMapping>
+ <item>
+ <deviceName>/dev/xvda</deviceName>
+ <ebs>
+ <volumeId>vol-1234567890abcdef0</volumeId>
+ <status>attached</status>
+ <attachTime>2015-12-22T10:44:09.000Z</attachTime>
+ <deleteOnTermination>true</deleteOnTermination>
+ </ebs>
+ </item>
+ </blockDeviceMapping>
+ <virtualizationType>hvm</virtualizationType>
+ <clientToken>xMcwG14507example</clientToken>
+ <tagSet>
+ <item>
+ <key>Name</key>
+ <value>Server_1</value>
+ </item>
+ </tagSet>
+ <hypervisor>xen</hypervisor>
+ <networkInterfaceSet>
+ <item>
+ <networkInterfaceId>eni-551ba033</networkInterfaceId>
+ <subnetId>subnet-56f5f633</subnetId>
+ <vpcId>vpc-11112222</vpcId>
+ <description>Primary network interface</description>
+ <ownerId>123456789012</ownerId>
+ <status>in-use</status>
+ <macAddress>02:dd:2c:5e:01:69</macAddress>
+ <privateIpAddress>192.168.1.88</privateIpAddress>
+ <privateDnsName>ip-192-168-1-88.eu-west-1.compute.internal</privateDnsName>
+ <sourceDestCheck>true</sourceDestCheck>
+ <groupSet>
+ <item>
+ <groupId>sg-e4076980</groupId>
+ <groupName>SecurityGroup1</groupName>
+ </item>
+ </groupSet>
+ <attachment>
+ <attachmentId>eni-attach-39697adc</attachmentId>
+ <deviceIndex>0</deviceIndex>
+ <status>attached</status>
+ <attachTime>2015-12-22T10:44:05.000Z</attachTime>
+ <deleteOnTermination>true</deleteOnTermination>
+ </attachment>
+ <association>
+ <publicIp>54.194.252.215</publicIp>
+ <publicDnsName>ec2-54-194-252-215.eu-west-1.compute.amazonaws.com</publicDnsName>
+ <ipOwnerId>amazon</ipOwnerId>
+ </association>
+ <privateIpAddressesSet>
+ <item>
+ <privateIpAddress>192.168.1.88</privateIpAddress>
+ <privateDnsName>ip-192-168-1-88.eu-west-1.compute.internal</privateDnsName>
+ <primary>true</primary>
+ <association>
+ <publicIp>54.194.252.215</publicIp>
+ <publicDnsName>ec2-54-194-252-215.eu-west-1.compute.amazonaws.com</publicDnsName>
+ <ipOwnerId>amazon</ipOwnerId>
+ </association>
+ </item>
+ </privateIpAddressesSet>
+ <ipv6AddressesSet>
+ <item>
+ <ipv6Address>2001:db8:1234:1a2b::123</ipv6Address>
+ </item>
+ </ipv6AddressesSet>
+ </item>
+ </networkInterfaceSet>
+ <ebsOptimized>false</ebsOptimized>
+ </item>
+ </instancesSet>
+ </item>
+ </reservationSet>
+</DescribeInstancesResponse>`))
+}
+
+func quitHandler(l net.Listener) func(http.ResponseWriter, *http.Request) {
+ return func(w http.ResponseWriter, r *http.Request) {
+ l.Close()
+ w.WriteHeader(http.StatusNoContent)
+ }
+}
diff --git a/test/integration/datasources_vault.bats b/test/integration/datasources_vault.bats
index aa6e27f3..90d1a3cc 100644
--- a/test/integration/datasources_vault.bats
+++ b/test/integration/datasources_vault.bats
@@ -15,9 +15,15 @@ path "*" {
}
EOF
tmpdir=$(mktemp -d)
+ cp ~/.vault-token ~/.vault-token.bak
+ start_meta_svc
+ start_aws_svc
}
function teardown () {
+ mv ~/.vault-token.bak ~/.vault-token
+ stop_meta_svc
+ stop_aws_svc
rm -rf $tmpdir
unset VAULT_TOKEN
vault delete secret/foo
@@ -27,6 +33,7 @@ function teardown () {
vault auth-disable approle2
vault auth-disable app-id
vault auth-disable app-id2
+ vault auth-disable aws
vault policy-delete writepol
vault policy-delete readpol
vault unmount ssh
@@ -122,6 +129,20 @@ function teardown () {
[[ "${output}" == "$BATS_TEST_DESCRIPTION" ]]
}
+@test "Testing ec2 vault auth" {
+ vault write secret/foo value="$BATS_TEST_DESCRIPTION"
+ vault auth-enable aws
+ vault write auth/aws/config/client secret_key=secret access_key=access endpoint=http://127.0.0.1:8082/ec2 iam_endpoint=http://127.0.0.1:8082/iam sts_endpoint=http://127.0.0.1:8082/sts
+ curl -o $tmpdir/certificate -s -f http://127.0.0.1:8081/certificate
+ vault write auth/aws/config/certificate/testcert type=pkcs7 aws_public_cert=@$tmpdir/certificate
+ vault write auth/aws/role/ami-00000000 auth_type=ec2 bound_ami_id=ami-00000000 policies=readpol
+ unset VAULT_TOKEN
+ rm ~/.vault-token
+ AWS_META_ENDPOINT=http://127.0.0.1:8081 gomplate -d vault=vault:///secret -i '{{(datasource "vault" "foo").value}}'
+ [ "$status" -eq 0 ]
+ [[ "${output}" == "$BATS_TEST_DESCRIPTION" ]]
+}
+
@test "Testing vault auth with dynamic secret" {
vault mount ssh
vault write ssh/roles/test key_type=otp default_user=user cidr_list=10.0.0.0/8
diff --git a/test/integration/helper.bash b/test/integration/helper.bash
index 41c2ac88..00867bc8 100644
--- a/test/integration/helper.bash
+++ b/test/integration/helper.bash
@@ -34,6 +34,22 @@ function stop_mirror_svc () {
wget -q http://127.0.0.1:8080/quit
}
+function start_meta_svc () {
+ bin/meta 3>/dev/null &
+}
+
+function stop_meta_svc () {
+ wget -q http://127.0.0.1:8081/quit
+}
+
+function start_aws_svc () {
+ bin/aws &
+}
+
+function stop_aws_svc () {
+ wget -q http://127.0.0.1:8082/quit
+}
+
function wait_for_url () {
url=$1
for i in {0..10}; do
@@ -55,4 +71,4 @@ function start_consul () {
function stop_consul () {
PID_FILE=/tmp/gomplate-test-consul.pid
kill $(cat $PID_FILE) &>/dev/null
-} \ No newline at end of file
+}
diff --git a/test/integration/metasvc/main.go b/test/integration/metasvc/main.go
new file mode 100644
index 00000000..4a1d2626
--- /dev/null
+++ b/test/integration/metasvc/main.go
@@ -0,0 +1,142 @@
+package main
+
+import (
+ "bytes"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/x509"
+ "crypto/x509/pkix"
+ "encoding/pem"
+ "flag"
+ "log"
+ "math/big"
+ "net"
+ "net/http"
+
+ "github.com/fullsailor/pkcs7"
+)
+
+var port string
+var priv *rsa.PrivateKey
+var derBytes []byte
+
+const instanceDocument = `{
+ "devpayProductCodes" : null,
+ "availabilityZone" : "xx-test-1b",
+ "privateIp" : "10.1.2.3",
+ "version" : "2010-08-31",
+ "instanceId" : "i-00000000000000000",
+ "billingProducts" : null,
+ "instanceType" : "t2.micro",
+ "accountId" : "1",
+ "imageId" : "ami-00000000",
+ "pendingTime" : "2000-00-01T0:00:00Z",
+ "architecture" : "x86_64",
+ "kernelId" : null,
+ "ramdiskId" : null,
+ "region" : "xx-test-1"
+}`
+
+func main() {
+ flag.StringVar(&port, "p", "8081", "Port to listen to")
+ flag.Parse()
+
+ certificateGenerate()
+
+ l, err := net.Listen("tcp", ":"+port)
+ if err != nil {
+ log.Fatal(err)
+ }
+ // defer l.Close()
+ http.HandleFunc("/", rootHandler)
+
+ http.HandleFunc("/latest/dynamic/instance-identity/pkcs7", pkcsHandler)
+ http.HandleFunc("/latest/dynamic/instance-identity/document", documentHandler)
+ http.HandleFunc("/certificate", certificateHandler)
+
+ http.HandleFunc("/quit", quitHandler(l))
+
+ http.Serve(l, nil)
+}
+
+func certificateGenerate() {
+ var err error
+ priv, err = rsa.GenerateKey(rand.Reader, 2048)
+ if err != nil {
+ log.Fatalf("failed to generate private key: %s", err)
+ }
+
+ serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
+ serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
+ if err != nil {
+ log.Fatalf("failed to generate serial number: %s", err)
+ }
+
+ template := x509.Certificate{
+ SerialNumber: serialNumber,
+ Subject: pkix.Name{
+ Organization: []string{"Test"},
+ },
+ }
+
+ derBytes, err = x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
+ if err != nil {
+ log.Fatalf("Failed to create certificate: %s", err)
+ }
+}
+
+func rootHandler(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/plain")
+ w.Write([]byte(""))
+}
+
+func pkcsHandler(w http.ResponseWriter, r *http.Request) {
+ cert, err := x509.ParseCertificate(derBytes)
+ if err != nil {
+ log.Fatalf("Cannot decode certificate: %s", err)
+ }
+
+ // Initialize a SignedData struct with content to be signed
+ signedData, err := pkcs7.NewSignedData([]byte(instanceDocument))
+ if err != nil {
+ log.Fatalf("Cannot initialize signed data: %s", err)
+ }
+
+ // Add the signing cert and private key
+ if err := signedData.AddSigner(cert, priv, pkcs7.SignerInfoConfig{}); err != nil {
+ log.Fatalf("Cannot add signer: %s", err)
+ }
+
+ // Finish() to obtain the signature bytes
+ detachedSignature, err := signedData.Finish()
+ if err != nil {
+ log.Fatalf("Cannot finish signing data: %s", err)
+ }
+
+ encoded := pem.EncodeToMemory(&pem.Block{Type: "PKCS7", Bytes: detachedSignature})
+
+ encoded = bytes.TrimPrefix(encoded, []byte("-----BEGIN PKCS7-----\n"))
+ encoded = bytes.TrimSuffix(encoded, []byte("\n-----END PKCS7-----\n"))
+
+ w.Header().Set("Content-Type", "text/plain")
+ w.Write(encoded)
+}
+
+func documentHandler(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.Write([]byte(instanceDocument))
+}
+
+func certificateHandler(w http.ResponseWriter, r *http.Request) {
+ encoded := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
+
+ w.Header().Set("Content-Type", "text/plain")
+ w.Write(encoded)
+}
+
+func quitHandler(l net.Listener) func(http.ResponseWriter, *http.Request) {
+ return func(w http.ResponseWriter, r *http.Request) {
+ l.Close()
+ w.WriteHeader(http.StatusNoContent)
+ }
+}