diff options
| author | Stuart Clark <stuart.clark@Jahingo.com> | 2017-08-08 02:20:56 +0100 |
|---|---|---|
| committer | Dave Henderson <dhenderson@gmail.com> | 2017-08-07 21:20:56 -0400 |
| commit | d2cf55b83fe71d41c3a09b35b280d9a48b24088d (patch) | |
| tree | cc152e6ed7d4f2346d179bb867a683e47a3d9852 /test | |
| parent | cbc2af15fccdfc43ae6dfc2ae2d88248df039ddb (diff) | |
Vault AWS EC2 auth (#190)
Diffstat (limited to 'test')
| -rw-r--r-- | test/integration/.gitignore | 2 | ||||
| -rw-r--r-- | test/integration/Dockerfile | 4 | ||||
| -rw-r--r-- | test/integration/awssvc/main.go | 190 | ||||
| -rw-r--r-- | test/integration/datasources_vault.bats | 21 | ||||
| -rw-r--r-- | test/integration/helper.bash | 18 | ||||
| -rw-r--r-- | test/integration/metasvc/main.go | 142 |
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) + } +} |
