Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

AWSのコンソルにパスワード無しログイン

More than 3 years have passed since last update.

はじめに

社内で社員や色んなサービスがTLSクライアント証明書を使います。社内のアイデンティティ管理システムとAWSに関係になりたいから、このGatewayのシステムを開発しました。

TLSクライアント証明書は不要。 SAML、OAuth2などがこの認証パタンを使う可能性です。

信用モデル

trust.png

Graphvizのソース

digraph trust {
  graph[style="filled" color="#ffffff" fontcolor="#333333" fontname="sans" fontsize="16" rankdir="LR"];
  node[shape="rect" color="#333333" fontcolor="#333333" fillcolor="#f8f8f8" style="filled" fontname="sans"];
  edge[fontname="sans" color="#79b74a" fontcolor="#333333" fontsize="12"];

  engineer[label="エンジニア"]
  gateway[label="Gateway"];
  ca[label="社内のCA"];
  iam[label="AWS IAM"]

  engineer -> gateway[label="TLS 相互認証" dir="both"];
  gateway -> ca[label="CRL/OCSP"];
  gateway -> iam[color="#eea236" label="sts:assumeRole"];
  iam -> gateway[color="#eea236" label="認証情報セット"];
}


このGatewayの責任は認証、そしてIAMは認証と認可です。二つのIAMロールがある:Gatewayのインスタンスプロファイルとエンジャニヤの個人的なロール。GatewayがTLSクライアント証明書のSubjectを読むからデータベースが不要です。絶対にGatewayがエンジニアの認証情報セットを保存してません。

アーキテクチャ

logical.png

Graphvizのソース

digraph architecture {
  graph[style="filled" color="#333333" fillcolor="#ffffff" style="filled" fontcolor="#333333" fontname="sans" fontsize="16" rankdir="LR"];
  node[shape="rect" color="#333333" fontcolor="#333333" fillcolor="#f8f8f8" style="filled" fontname="sans"];
  edge[fontname="sans" color="#79b74a" fontcolor="#333333" fontsize="12"];

  engineer[label="エンジニア" color="#ffffff" fillcolor="#ffffff"];
  iam[label="IAM" color="#ffffff" fillcolor="#ffffff"];

  subgraph cluster0{
    graph[label="VPC" labelloc="b" style="dashed"];
    elb[label="ELB" fixedsize="true" height="2" width="0.5"];
    subgraph cluster1 {
      graph[label="EC2" labelloc="t" labeljust="r" style="filled"];
      apache[label="Apache"];
      gateway[label="Gateway"];
    }
    nat[label="NAT"];
  }
  engineer -> elb[label="https://gateway/"];
  elb -> apache[];
  apache -> gateway[color="#eea236"];
  gateway -> nat[dir="none"];
  nat -> iam[];
}

API GatewayがTLSクライアント証明書が使わないからELBとEC2は必要です。そしてTLSのセッションがエンジニアからApacheまで、ELBのリスナーはTCP:TCPです。Proxy Protocol のサポートとApacheのmod_proxy_protocolを設定しない限り、クライアントのIPアドレスは受け取りません。

ログインのシーケンス

diagram-4989406697349261473.png

PlantUMLのソース

@startuml

skinparam monochrome true
skinparam shadowing false

エンジニア -> Gateway: /login?account=1234
Gateway -> Gateway: Validate Certificate \n (CRL/OCSPなど)
Gateway -> STS: assumeRole
STS -> Gateway: 認証情報セット
Gateway -> エンジニア: 302 https://signin.aws.amazon.com/federation?
エンジニア -> "フェデレーションエンドポイント": GET /federation
"フェデレーションエンドポイント" -> エンジニア: 302 https://console.aws.amazon.com/
エンジニア -> コンソール: GET /
コンソール -> エンジニア: 200

@enduml


上の図は、最後のリクエストから追加のリクエストがありますから完成しません。

開発のメモ

リバースプロキシサーバーの設定

Listen 443
<VirtualHost *:443>

SSLEngine On
SSLVerifyClient require
SSLVerifyDepth 2

# 他の設定はこちら

RequestHeader unset client-ssl-certificate 
RequestHeader set client-ssl-certificate "/%{SSL_CLIENT_S_DN_O}s/%{SSL_CLIENT_S_DN_OU}s/%{SSL_CLIENT_S_DN_Email}s/"

ProxyPass / https://localhost:8080
ProxyPassReverse / https://localhost:8080
</VirtualHost>

そのRequestHeaderのunsetの前にsetはヘッダインジェクションの対策です。

Gatewayの例

'use strict';

var express = require('express'),
    AWS     = require('aws-sdk');

var app = express();

app.get('/login', function(req, res){

  var params = {
    'RoleArn': 'arn:aws:iam::' + req.query.account + ':role/' + req.header('x-ssl-certificate'),
    'RoleSessionName': 'Gateway'
  };

  sts.assumeRole(params, function(err, data) {
    if(err){
      res.status(400).send('Access Denied.').end();
    } else {
        var signInToken = encodeURIComponent(JSON.stringify({
            'sessionId':    data.credentials.accessKeyId,
            'sessionKey':   data.credentials.secretAccessKey,
            'sessionToken': data.credentials.sessionToken
        }));
        var redirectUrl = [
          'https://signin.aws.amazon.com/federation',
          '?Action=login',
          '&Issuer='     , encodeURIComponent('https://gateway/'),
          '&Destination=', encodeURIComponent('https://console.aws.amazon.com/'),
          '&SigninToken=', signInToken].join('');
        res.redirect(302, redirectUrl).end();
    }
  });
});

app.listen(8080);

エンジニアのロール

AWSのアカウントで、全てのエンジニアのために、新しいのロールを設定するべきです。

CloudFormation例

{
  "Type": "AWS::IAM::Role",
  "Properties": {
    "RoleName": "pnktsrmn",
    "Path": "/Company_CA/Department_OU/pnktsrmn@himitsukaisha.com/",
    "AssumeRolePolicyDocument": {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "",
          "Effect": "Allow",
          "Principal": {
            "AWS": "arn:aws:iam::1234567890:role/gateway-role"
          },
          "Action": "sts:AssumeRole"
        }
      ]
    }
  }
}

PrincipalはGatewayのインスタンスプロファイルのARN。パスはTLSクライアント証明書のSubjectです。

参考

pnktsrmn
私はアーキテクトです。セキュリティ、クラウド、などが興味あります。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away