mirror of https://github.com/kubernetes/kops.git
Merge pull request #2449 from justinsb/fix_automatic_hosted_zone_creation
Fix automatic private DNS zone creation
This commit is contained in:
commit
dd824cb679
|
@ -38,7 +38,6 @@ type KopsModelContext struct {
|
|||
Cluster *kops.Cluster
|
||||
|
||||
Region string
|
||||
HostedZoneID string // used to set up route53 IAM policy
|
||||
InstanceGroups []*kops.InstanceGroup
|
||||
|
||||
SSHPublicKeys [][]byte
|
||||
|
|
|
@ -19,6 +19,7 @@ package model
|
|||
import (
|
||||
"fmt"
|
||||
"k8s.io/kops/pkg/apis/kops"
|
||||
"k8s.io/kops/pkg/dns"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awstasks"
|
||||
"strings"
|
||||
|
@ -72,6 +73,15 @@ func (b *DNSModelBuilder) Build(c *fi.ModelBuilderContext) error {
|
|||
if err := b.ensureDNSZone(c); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// We now create the DNS Zone for AWS even in the case of public zones;
|
||||
// it has to exist for the IAM record anyway.
|
||||
// TODO: We can now rationalize the code paths
|
||||
if !dns.IsGossipHostname(b.Cluster.Name) {
|
||||
if err := b.ensureDNSZone(c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if b.UseLoadBalancerForAPI() {
|
||||
|
|
|
@ -19,6 +19,7 @@ package model
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kops/pkg/apis/kops"
|
||||
"k8s.io/kops/pkg/model/iam"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
|
@ -80,15 +81,30 @@ func (b *IAMModelBuilder) Build(c *fi.ModelBuilderContext) error {
|
|||
|
||||
}
|
||||
|
||||
policy, err := b.buildAWSIAMPolicy(role)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
{
|
||||
iamPolicy := &iam.IAMPolicyResource{
|
||||
Builder: &iam.IAMPolicyBuilder{
|
||||
Cluster: b.Cluster,
|
||||
Role: role,
|
||||
Region: b.Region,
|
||||
},
|
||||
}
|
||||
|
||||
// This is slightly tricky; we need to know the hosted zone id,
|
||||
// but we might be creating the hosted zone dynamically.
|
||||
|
||||
// TODO: I don't love this technique for finding the task by name & modifying it
|
||||
dnsZoneTask, found := c.Tasks["DNSZone/"+b.NameForDNSZone()]
|
||||
if found {
|
||||
iamPolicy.DNSZone = dnsZoneTask.(*awstasks.DNSZone)
|
||||
} else {
|
||||
glog.V(2).Infof("Task %q not found; won't set route53 permissions in IAM", "DNSZone/"+b.NameForDNSZone())
|
||||
}
|
||||
|
||||
t := &awstasks.IAMRolePolicy{
|
||||
Name: s(name),
|
||||
Role: iamRole,
|
||||
PolicyDocument: fi.WrapResource(fi.NewStringResource(policy)),
|
||||
PolicyDocument: iamPolicy,
|
||||
}
|
||||
c.AddTask(t)
|
||||
}
|
||||
|
@ -154,26 +170,6 @@ func (b *IAMModelBuilder) Build(c *fi.ModelBuilderContext) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// buildAWSIAMPolicy produces the AWS IAM policy for the given role
|
||||
func (b *IAMModelBuilder) buildAWSIAMPolicy(role kops.InstanceGroupRole) (string, error) {
|
||||
pb := &iam.IAMPolicyBuilder{
|
||||
Cluster: b.Cluster,
|
||||
Role: role,
|
||||
Region: b.Region,
|
||||
HostedZoneID: b.HostedZoneID,
|
||||
}
|
||||
|
||||
policy, err := pb.BuildAWSIAMPolicy()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error building IAM policy: %v", err)
|
||||
}
|
||||
json, err := policy.AsJSON()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error building IAM policy: %v", err)
|
||||
}
|
||||
return json, nil
|
||||
}
|
||||
|
||||
// buildAWSIAMRolePolicy produces the AWS IAM role policy for the given role
|
||||
func (b *IAMModelBuilder) buildAWSIAMRolePolicy() (fi.Resource, error) {
|
||||
functions := template.FuncMap{
|
||||
|
|
|
@ -17,14 +17,18 @@ limitations under the License.
|
|||
package iam
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
api "k8s.io/kops/pkg/apis/kops"
|
||||
"k8s.io/kops/pkg/util/stringorslice"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/awstasks"
|
||||
"k8s.io/kops/util/pkg/vfs"
|
||||
)
|
||||
|
||||
|
@ -175,25 +179,9 @@ func (b *IAMPolicyBuilder) BuildAWSIAMPolicy() (*IAMPolicy, error) {
|
|||
}
|
||||
}
|
||||
|
||||
p.Statement = append(p.Statement, &IAMStatement{
|
||||
Effect: IAMStatementEffectAllow,
|
||||
Action: stringorslice.Of("route53:ChangeResourceRecordSets",
|
||||
"route53:ListResourceRecordSets",
|
||||
"route53:GetHostedZone"),
|
||||
Resource: stringorslice.Slice([]string{"arn:aws:route53:::hostedzone/" + b.HostedZoneID}),
|
||||
})
|
||||
|
||||
p.Statement = append(p.Statement, &IAMStatement{
|
||||
Effect: IAMStatementEffectAllow,
|
||||
Action: stringorslice.Slice([]string{"route53:GetChange"}),
|
||||
Resource: stringorslice.Slice([]string{"arn:aws:route53:::change/*"}),
|
||||
})
|
||||
|
||||
p.Statement = append(p.Statement, &IAMStatement{
|
||||
Effect: IAMStatementEffectAllow,
|
||||
Action: stringorslice.Slice([]string{"route53:ListHostedZones"}),
|
||||
Resource: wildcard,
|
||||
})
|
||||
if b.HostedZoneID != "" {
|
||||
addRoute53Permissions(p, b.HostedZoneID)
|
||||
}
|
||||
|
||||
// For S3 IAM permissions, we grant permissions to subtrees. So find the parents;
|
||||
// we don't need to grant mypath and mypath/child.
|
||||
|
@ -273,6 +261,33 @@ func (b *IAMPolicyBuilder) BuildAWSIAMPolicy() (*IAMPolicy, error) {
|
|||
return p, nil
|
||||
}
|
||||
|
||||
func addRoute53Permissions(p *IAMPolicy, hostedZoneID string) {
|
||||
// Remove /hostedzone/ prefix (if present)
|
||||
hostedZoneID = strings.TrimPrefix(hostedZoneID, "/")
|
||||
hostedZoneID = strings.TrimPrefix(hostedZoneID, "hostedzone/")
|
||||
|
||||
p.Statement = append(p.Statement, &IAMStatement{
|
||||
Effect: IAMStatementEffectAllow,
|
||||
Action: stringorslice.Of("route53:ChangeResourceRecordSets",
|
||||
"route53:ListResourceRecordSets",
|
||||
"route53:GetHostedZone"),
|
||||
Resource: stringorslice.Slice([]string{"arn:aws:route53:::hostedzone/" + hostedZoneID}),
|
||||
})
|
||||
|
||||
p.Statement = append(p.Statement, &IAMStatement{
|
||||
Effect: IAMStatementEffectAllow,
|
||||
Action: stringorslice.Slice([]string{"route53:GetChange"}),
|
||||
Resource: stringorslice.Slice([]string{"arn:aws:route53:::change/*"}),
|
||||
})
|
||||
|
||||
wildcard := stringorslice.Slice([]string{"*"})
|
||||
p.Statement = append(p.Statement, &IAMStatement{
|
||||
Effect: IAMStatementEffectAllow,
|
||||
Action: stringorslice.Slice([]string{"route53:ListHostedZones"}),
|
||||
Resource: wildcard,
|
||||
})
|
||||
}
|
||||
|
||||
// IAMPrefix returns the prefix for AWS ARNs in the current region, for use with IAM
|
||||
// it is arn:aws everywhere but in cn-north, where it is arn:aws-cn
|
||||
func (b *IAMPolicyBuilder) IAMPrefix() string {
|
||||
|
@ -283,3 +298,40 @@ func (b *IAMPolicyBuilder) IAMPrefix() string {
|
|||
return "arn:aws"
|
||||
}
|
||||
}
|
||||
|
||||
type IAMPolicyResource struct {
|
||||
Builder *IAMPolicyBuilder
|
||||
DNSZone *awstasks.DNSZone
|
||||
}
|
||||
|
||||
var _ fi.Resource = &IAMPolicyResource{}
|
||||
var _ fi.HasDependencies = &IAMPolicyResource{}
|
||||
|
||||
func (b *IAMPolicyResource) GetDependencies(tasks map[string]fi.Task) []fi.Task {
|
||||
return []fi.Task{b.DNSZone}
|
||||
}
|
||||
|
||||
// Open produces the AWS IAM policy for the given role
|
||||
func (b *IAMPolicyResource) Open() (io.Reader, error) {
|
||||
// Defensive copy before mutation
|
||||
pb := *b.Builder
|
||||
|
||||
if b.DNSZone != nil {
|
||||
hostedZoneID := fi.StringValue(b.DNSZone.ZoneID)
|
||||
if hostedZoneID == "" {
|
||||
// Dependency analysis failure?
|
||||
return nil, fmt.Errorf("DNS ZoneID not set")
|
||||
}
|
||||
pb.HostedZoneID = hostedZoneID
|
||||
}
|
||||
|
||||
policy, err := pb.BuildAWSIAMPolicy()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error building IAM policy: %v", err)
|
||||
}
|
||||
json, err := policy.AsJSON()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error building IAM policy: %v", err)
|
||||
}
|
||||
return bytes.NewReader([]byte(json)), nil
|
||||
}
|
||||
|
|
|
@ -399,11 +399,6 @@ func (c *ApplyClusterCmd) Run() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dnszone, err := findZone(cluster, cloud)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
modelContext.HostedZoneID = dnszone.ID()
|
||||
}
|
||||
|
||||
clusterTags, err := buildCloudupTags(cluster)
|
||||
|
|
|
@ -40,7 +40,7 @@ type IAMRolePolicy struct {
|
|||
|
||||
// The PolicyDocument to create as an inline policy.
|
||||
// If the PolicyDocument is empty, the policy will be removed.
|
||||
PolicyDocument *fi.ResourceHolder
|
||||
PolicyDocument fi.Resource
|
||||
}
|
||||
|
||||
func (e *IAMRolePolicy) Find(c *fi.Context) (*IAMRolePolicy, error) {
|
||||
|
@ -97,7 +97,7 @@ func (s *IAMRolePolicy) CheckChanges(a, e, changes *IAMRolePolicy) error {
|
|||
}
|
||||
|
||||
func (_ *IAMRolePolicy) ShouldCreate(a, e, changes *IAMRolePolicy) (bool, error) {
|
||||
ePolicy, err := e.PolicyDocument.AsString()
|
||||
ePolicy, err := e.policyDocumentString()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("error rendering PolicyDocument: %v", err)
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ func (_ *IAMRolePolicy) ShouldCreate(a, e, changes *IAMRolePolicy) (bool, error)
|
|||
}
|
||||
|
||||
func (_ *IAMRolePolicy) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *IAMRolePolicy) error {
|
||||
policy, err := e.PolicyDocument.AsString()
|
||||
policy, err := e.policyDocumentString()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error rendering PolicyDocument: %v", err)
|
||||
}
|
||||
|
@ -143,12 +143,9 @@ func (_ *IAMRolePolicy) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *IAMRoleP
|
|||
if changes.PolicyDocument != nil {
|
||||
glog.V(2).Infof("Applying changed role policy to %q:", *e.Name)
|
||||
|
||||
actualPolicy := ""
|
||||
if a.PolicyDocument != nil {
|
||||
actualPolicy, err = a.PolicyDocument.AsString()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading actual policy document: %v", err)
|
||||
}
|
||||
actualPolicy, err := a.policyDocumentString()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading actual policy document: %v", err)
|
||||
}
|
||||
|
||||
if actualPolicy == policy {
|
||||
|
@ -180,6 +177,13 @@ func (_ *IAMRolePolicy) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *IAMRoleP
|
|||
return nil // No tags in IAM
|
||||
}
|
||||
|
||||
func (e *IAMRolePolicy) policyDocumentString() (string, error) {
|
||||
if e.PolicyDocument == nil {
|
||||
return "", nil
|
||||
}
|
||||
return fi.ResourceAsString(e.PolicyDocument)
|
||||
}
|
||||
|
||||
type terraformIAMRolePolicy struct {
|
||||
Name *string `json:"name"`
|
||||
Role *terraform.Literal `json:"role"`
|
||||
|
@ -187,15 +191,14 @@ type terraformIAMRolePolicy struct {
|
|||
}
|
||||
|
||||
func (_ *IAMRolePolicy) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *IAMRolePolicy) error {
|
||||
{
|
||||
policyString, err := e.PolicyDocument.AsString()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error rendering PolicyDocument: %v", err)
|
||||
}
|
||||
if policyString == "" {
|
||||
// A deletion; we simply don't render; terraform will observe the removal
|
||||
return nil
|
||||
}
|
||||
policyString, err := e.policyDocumentString()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error rendering PolicyDocument: %v", err)
|
||||
}
|
||||
|
||||
if policyString == "" {
|
||||
// A deletion; we simply don't render; terraform will observe the removal
|
||||
return nil
|
||||
}
|
||||
|
||||
policy, err := t.AddFile("aws_iam_role_policy", *e.Name, "policy", e.PolicyDocument)
|
||||
|
@ -223,15 +226,13 @@ type cloudformationIAMRolePolicy struct {
|
|||
}
|
||||
|
||||
func (_ *IAMRolePolicy) RenderCloudformation(t *cloudformation.CloudformationTarget, a, e, changes *IAMRolePolicy) error {
|
||||
{
|
||||
policyString, err := e.PolicyDocument.AsString()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error rendering PolicyDocument: %v", err)
|
||||
}
|
||||
if policyString == "" {
|
||||
// A deletion; we simply don't render; cloudformation will observe the removal
|
||||
return nil
|
||||
}
|
||||
policyString, err := e.policyDocumentString()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error rendering PolicyDocument: %v", err)
|
||||
}
|
||||
if policyString == "" {
|
||||
// A deletion; we simply don't render; cloudformation will observe the removal
|
||||
return nil
|
||||
}
|
||||
|
||||
tf := &cloudformationIAMRolePolicy{
|
||||
|
@ -240,13 +241,8 @@ func (_ *IAMRolePolicy) RenderCloudformation(t *cloudformation.CloudformationTar
|
|||
}
|
||||
|
||||
{
|
||||
jsonString, err := e.PolicyDocument.AsBytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data := make(map[string]interface{})
|
||||
err = json.Unmarshal(jsonString, &data)
|
||||
err = json.Unmarshal([]byte(policyString), &data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error parsing PolicyDocument: %v", err)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue