2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[AWS] Verified AccessとOIDCによるアクセス制御

Posted at

はじめに

  • Verified Accessはプライベートアプリケーション(LB等)に対するVPNレスでのアクセス制御を実施するためのAWSサービスです。
  • OIDCのIdPと連携することで、認証ステータスやIdP側のユーザグループに応じた細かな認証認可を実装することも出来ます。
  • OIDCのIdPとなるKeycloak及びAWS資源を実際に設定し、認証認可の動作確認をしてみます。(AWS資源設定にはAWS CDKを利用します。)

OIDC認証基盤設定

  • 既存の構築済みKeycloakに対して、Verfied Accessと連携するための設定をしていきます。

本記事ではKeycloak実行環境構築の詳細は割愛します。

Client設定

  • Keycloak管理コンソールのClientsから、OIDC Clientを作成します。
    • Valid redirect URIs
      • https://Verified Accessで設定するカスタムドメイン/oauth2/idpresponse
  • Keycloak管理コンソールのClients scopesから、client scopeを作成します。
    • Mapper type
      • Group Membership
    • Name
      • groups
    • Token Claim Name
      • groups

image.png

image.png

  • Keycloak管理コンソールのClientsから、上記で作成したOIDC Clientを選択してClient scopesタブを選択し、groupsをAdd client scopeします。
    image.png

ユーザ設定

  • Keycloak管理コンソールのGroupsから、動作確認用グループを作成します。
  • Keycloak管理コンソールのUsersから、動作確認用ユーザを作成します。
  • 動作確認用ユーザの詳細画面にてGroupsタブを選択し、動作確認用グループをアタッチします。
  • Keycloak管理コンソールのClientsから、上記で作成したOIDC Clientを選択してClient scopesタブを選択し、Evaluateを選択します。動作確認用ユーザを選択すると、各種トークンやUserInfoエンドポイント提供情報にgroups情報が内包されていることが分かります。

OIDC情報確認

  • Keycloak管理コンソールのClientsから、先ほど作成したClientを選択し、clientIdを控えておきます。
  • Credentialsタブを選択し、clientSecretを控えておきます。
  • Keycloak管理コンソールのRealm settings から、OpenID Endpoint Configuration にアクセスします。
  • 遷移先のOpenID Configurationエンドポイントで、issuerと各種エンドポイント情報を控えておきます。
    • authorizationEndpoint
    • issuer
    • tokenEndpoint
    • userInfoEndpoint

ネットワーク設定

バックエンドアプリ設定

  • 内部向けALB及びLambda関数から構成されるバックエンドアプリ資源を設定します。
    • 検証用のため、内部向けALBのプロトコルはHTTPにしています。
// ---- Lambda
const itemLambda = new lambda.Function(this, "ItemLambda", {
  functionName: `${props.prefix}-lambda-backend-item`,
  runtime: lambda.Runtime.PYTHON_3_12,
  code: lambda.Code.fromAsset('lambda/python/backend'),
  handler: 'test.lambda_handler',
  memorySize: 256,
  tracing: lambda.Tracing.ACTIVE,
  insightsVersion: lambda.LambdaInsightsVersion.VERSION_1_0_98_0,
  vpc: props.vpc,
  vpcSubnets: props.vpc.selectSubnets({
    subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS
  })
})
// ---- ALB
// Security Group
const backendAlbSecurityGroup = new ec2.SecurityGroup(this, "BackendAlbSecurityGroup", {
  securityGroupName: `${props.prefix}-sg-alb-back`,
  vpc: props.vpc,
  description: "Security Group for Backend ALB"
})
backendAlbSecurityGroup.addIngressRule(
  ec2.Peer.anyIpv4(),
  ec2.Port.tcp(80),
  "Allow Inbound HTTP Access"
)
backendAlbSecurityGroup.addEgressRule(
  ec2.Peer.ipv4(props.vpc.vpcCidrBlock),
  ec2.Port.tcp(443),
  "Allow Outbound HTTPS Access to inside of VPC"
)
backendAlbSecurityGroup.addEgressRule(
  ec2.Peer.ipv4(props.vpc.vpcCidrBlock),
  ec2.Port.tcp(80),
  "Allow Outbound HTTP Access to inside of VPC"
)
// Alb
this.alb = new elbv2.ApplicationLoadBalancer(this, 'Alb', {
  vpc: props.vpc,
  vpcSubnets: props.vpc.selectSubnets({
    subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS
  }),
  loadBalancerName: `${props.prefix}-alb-back`,
  internetFacing: false,
  securityGroup: backendAlbSecurityGroup,
});
// Listener
const albListener = this.alb.addListener('AlbListener', {
  protocol: elbv2.ApplicationProtocol.HTTP,
  port: 80,
});
// Target
albListener.addTargets(
  "LambdaTarget",
  {
    targetGroupName: `${props.prefix}-tg-back-lambda`,
    targets: [new elbv2Targets.LambdaTarget(itemLambda)],
    healthCheck: {
      enabled: true,
    }
  }
);
import json

def lambda_handler(event, context):
  return {
    "statusCode": 200,
    "statusDescription": "200 OK",
    "headers": {
      "Content-Type": "text/html"
    },
    "isBase64Encoded": False,
    "body": "<h1>Hello from Lambda!</h1>"
  }

Verified Accessの設定

事前準備

  • Verified Accessエンドポイントで利用するSecurity Groupを作成します。
    // Security Group    	
    const verifiedSecurityGroup = new ec2.SecurityGroup(this, "VerifiedSecurityGroup", {
      securityGroupName: `${props.prefix}-sg-verified`,
      vpc: props.vpc,
      description: "Security Group for Verified Access"
    })  verifiedSecurityGroup.addIngressRule(
      ec2.Peer.anyIpv4(),
      ec2.Port.tcp(443),
      "Allow Inbound HTTPS Access"
    )
  • Verified Accessエンドポイントで利用するパブリック証明書をACMで作成します。
    // ACM
    const verifiedAcm = new acm.Certificate(this, "VerifiedAcm", {
      domainName: props.domainName,
      validation: acm.CertificateValidation.fromDns(props.hostedZone)
    })

信頼プロバイダー設定

  • Keycloak管理コンソールで取得した情報を用いて、信頼プロバイダーを作成します。
    // Trust Provider
    const verifiedAccessTrustProvider = new ec2.CfnVerifiedAccessTrustProvider(this, "VerifiedAccessTrustProvider", {
      tags: [{
        key: 'Name',
        value: `${props.prefix}-verified-access-trust-provider-oidc`,
      }],
      description: "OIDC VerifiedAccessTrustProvider",
      policyReferenceName: `${props.prefix}_user_trust_policy`,
      trustProviderType: "user",
      userTrustProviderType: "oidc",
      oidcOptions: {
        authorizationEndpoint: props.authorizationEndpoint,
        clientId: props.clientId,
        clientSecret: props.clientSecret,
        issuer: props.issuer,
        scope: "openid profile groups",
        tokenEndpoint: props.tokenEndpoint,
        userInfoEndpoint: props.userInfoEndpoint,
      }
    })

インスタンス設定

  • 上記で作成した信頼プロバイダーをアタッチしたインスタンスを作成します。
    // Instance
    const verifiedAccessInstance = new ec2.CfnVerifiedAccessInstance(this, "VerifiedAccessInstance", {
      tags: [{
        key: 'Name',
        value: `${props.prefix}-verified-access-instance-oidc`,
      }],
      description: "OIDC VerifiedAccessInstance",
      verifiedAccessTrustProviderIds: [verifiedAccessTrustProvider.attrVerifiedAccessTrustProviderId]
    })

グループ設定

  • 上記で作成したインスタンスをアタッチしたVerified Accessグループを作成します。
  • まずは、Keycloak認証済みユーザに対してアクセスを許可するよう、設定します。
    // Access Group
    const verifiedAccessGroup = new ec2.CfnVerifiedAccessGroup(this, "VerifiedAccessGroup", {
      tags: [{
        key: 'Name',
        value: `${props.prefix}-verified-access-group-oidc`,
      }],
      description: "OIDC VerifiedAccessGroup",
      verifiedAccessInstanceId: verifiedAccessInstance.attrVerifiedAccessInstanceId,
      policyDocument: "permit(principal,action,resource) when {true};"
    }

エンドポイント設定

上記で作成したグループ・ACM・Security Group・ALBをアタッチしたエンドポイントを作成します。

    // Access Endpoint
    const verifiedAccessEndpoint = new ec2.CfnVerifiedAccessEndpoint(this, "VerifiedAccessEndpoint", {
      tags: [{
        key: 'Name',
        value: `${props.prefix}-verified-access-endpoint-oidc`,
      }],
      description: "OIDC VerifiedAccessEndpoint",
      verifiedAccessGroupId: verifiedAccessGroup.attrVerifiedAccessGroupId,
      applicationDomain: props.domainName,
      domainCertificateArn: verifiedAcm.certificateArn,
      attachmentType: "vpc",
      securityGroupIds: [verifiedSecurityGroup.securityGroupId],
      endpointDomainPrefix: props.domainName.split(".")[0],
      endpointType: "load-balancer",
      loadBalancerOptions: {
        loadBalancerArn: props.LoadBalancerArn,
        port: 80,
        protocol: "http",
        subnetIds: props.vpc.selectSubnets({
          subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS
        }).subnetIds
      }
    })

Route53設定

  • Verified Accessにアクセスするためのカスタムドメインについて、CNAMEレコードとして設定します。
    // Route53 CNAME Record
    new route53.CnameRecord(this, "VerifiedAccessCnameRecord", {
      zone: props.hostedZone,
      recordName: props.domainName,
      domainName: verifiedAccessEndpoint.attrEndpointDomain,
    });

動作確認

  • カスタムドメインに対してアクセスすると、Keycloakの認証画面にリダイレクトされます。

image.png

  • Keycloakで認証を済ますと、無事にLambda関数からレスポンスが返却されます。

image.png

  • グループポリシーを設定変更し、特定のグループに所属している場合のみアクセスを許可するよう設定します。
permit(principal,action,resource)
when {
    context.poc_user_trust_policy.groups.contains("/XXX")
}; 
  • 上記設定後、当該グループに未所属のユーザでアクセスした場合には403エラーが返却されました。(反映までタイムラグがありました。)

image.png

  • 特定のグループに所属のユーザでは、引き続きアクセス可能なことも確認済みです。

注意事項

  • 本記事は万全を期して作成していますが、お気づきの点がありましたら、ご連絡よろしくお願いします。
  • なお、本記事の内容を利用した結果及び影響について、筆者は一切の責任を負いませんので、予めご了承ください。
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?