1
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?

GitHub Enterprise Server & Entra IDとのSSO設定

Last updated at Posted at 2024-12-28

GitHub Enterprise Server (GHES) & Entra IDでSSOを設定する手順。
備忘録として纏めます。

前提

  • GitHub Enterprise Server (GHES) ライセンスを持っていること
  • Entra ID 管理者権限を持っていること

GHES用 仮想マシンの作成

AzureでGHES用の仮想マシンを構築します。
以下の最小要件を参照しながら進めます。

今回は検証用途で、作ったり壊してを繰り返したかったのでIaCに。
以下、Terraform 👇️

サンプルコード
terraform.tf
terraform {
  required_version = ">=0.12"

  required_providers {
    azapi = {
      source  = "azure/azapi"
    }
    azurerm = {
      source  = "hashicorp/azurerm"
    }
    random = {
      source  = "hashicorp/random"
    }
  }
}

provider "azurerm" {
  features {}
}
variables.tf
variable "location" {
  type = string
  default = "japaneast"
}

variable "prefix" {
  type = string
  default = "ghes"
}

variable "resource_group_name" {
  type = string
  default = "rg-ghes"
}

variable "host_name" {
    type = string
    description = "value of vm host name"
    default = "×××××××××××"
}

variable "username" {
  type = string
  default = "azureuser"
}
ssh.tf
resource "random_pet" "ssh_key_name" {
  prefix    = "ssh"
  separator = ""
}

resource "azapi_resource_action" "ssh_public_key_gen" {
  type        = "Microsoft.Compute/sshPublicKeys@2022-11-01"
  resource_id = azapi_resource.ssh_public_key.id
  action      = "generateKeyPair"
  method      = "POST"

  response_export_values = ["publicKey", "privateKey"]
}

resource "azapi_resource" "ssh_public_key" {
  type      = "Microsoft.Compute/sshPublicKeys@2022-11-01"
  name      = random_pet.ssh_key_name.id
  location  = azurerm_resource_group.rg.location
  parent_id = azurerm_resource_group.rg.id
}

resource "local_file" "public_key_file" {
  content =  azapi_resource_action.ssh_public_key_gen.output.publicKey
  filename = "./id_rsa.pub"
}

resource "local_file" "private_key_file" {
  content  = azapi_resource_action.ssh_public_key_gen.output.privateKey
  filename = "./id_rsa"
}
main.tf
resource "azurerm_resource_group" "rg" {
  location = var.location
  name     = var.resource_group_name
}

# Vnet
resource "azurerm_virtual_network" "vnet" {
  address_space       = ["10.0.0.0/16"]
  location            = var.location
  name                = "vnet-${var.prefix}"
  resource_group_name = azurerm_resource_group.rg.name
}

resource "azurerm_subnet" "subnet_default" {
  address_prefixes     = ["10.0.0.0/24"]
  name                 = "default"
  resource_group_name  = var.resource_group_name
  virtual_network_name = azurerm_virtual_network.vnet.name
}

resource "azurerm_public_ip" "public_ip" {
  allocation_method   = "Static"
  domain_name_label   = var.host_name
  location            = var.location
  name                = "ip-${var.prefix}"
  resource_group_name = var.resource_group_name
  sku                 = "Standard"
  depends_on = [
    azurerm_resource_group.rg,
  ]
}

resource "azurerm_network_interface" "nic" {
  location            = var.location
  name                = "nic-${var.prefix}"
  resource_group_name = var.resource_group_name

  ip_configuration {
    name                          = "ipconfig1"
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = azurerm_public_ip.public_ip.id
    subnet_id                     = azurerm_subnet.subnet_default.id
  }
}

resource "azurerm_network_security_group" "nsg" {
  location            = var.location
  name                = "nsg-${var.prefix}"
  resource_group_name = var.resource_group_name
  depends_on = [
    azurerm_resource_group.rg,
  ]
}

resource "azurerm_network_interface_security_group_association" "nsgassoc" {
  network_interface_id      = azurerm_network_interface.nic.id
  network_security_group_id = azurerm_network_security_group.nsg.id
}

resource "azurerm_network_security_rule" "allow_http_inbound" {
  access                      = "Allow"
  destination_address_prefix  = "*"
  destination_port_range      = "80"
  direction                   = "Inbound"
  name                        = "AllowAnyHTTPInbound"
  network_security_group_name = azurerm_network_security_group.nsg.name
  priority                    = 1025
  protocol                    = "Tcp"
  resource_group_name         = var.resource_group_name
  source_address_prefix       = "*"
  source_port_range           = "*"
}

resource "azurerm_network_security_rule" "allow_git_end_user" {
  access                      = "Allow"
  destination_address_prefix  = "*"
  destination_port_range      = "9418"
  direction                   = "Inbound"
  name                        = "Git-End_user"
  network_security_group_name = azurerm_network_security_group.nsg.name
  priority                    = 1050
  protocol                    = "Tcp"
  resource_group_name         = var.resource_group_name
  source_address_prefix       = "*"
  source_port_range           = "*"
}

resource "azurerm_network_security_rule" "allow_https_end_user" {
  access                      = "Allow"
  destination_address_prefix  = "*"
  destination_port_range      = "443"
  direction                   = "Inbound"
  name                        = "HTTPS-End_user"
  network_security_group_name = azurerm_network_security_group.nsg.name
  priority                    = 1030
  protocol                    = "Tcp"
  resource_group_name         = var.resource_group_name
  source_address_prefix       = "*"
  source_port_range           = "*"
}

resource "azurerm_network_security_rule" "allow_https_management_console" {
  access                      = "Allow"
  destination_address_prefix  = "*"
  destination_port_range      = "8443"
  direction                   = "Inbound"
  name                        = "HTTPS-Management_console"
  network_security_group_name = azurerm_network_security_group.nsg.name
  priority                    = 1010
  protocol                    = "Tcp"
  resource_group_name         = var.resource_group_name
  source_address_prefix       = "*"
  source_port_range           = "*"
}

resource "azurerm_network_security_rule" "allow_ssh_end_user" {
  access                      = "Allow"
  destination_address_prefix  = "*"
  destination_port_range      = "22"
  direction                   = "Inbound"
  name                        = "SSH-End_user"
  network_security_group_name = azurerm_network_security_group.nsg.name
  priority                    = 1040
  protocol                    = "Tcp"
  resource_group_name         = var.resource_group_name
  source_address_prefix       = "*"
  source_port_range           = "*"
}

resource "azurerm_network_security_rule" "allow_ssh_management_console" {
  access                      = "Allow"
  destination_address_prefix  = "*"
  destination_port_range      = "122"
  direction                   = "Inbound"
  name                        = "SSH-Management_console"
  network_security_group_name = azurerm_network_security_group.nsg.name
  priority                    = 1020
  protocol                    = "Tcp"
  resource_group_name         = var.resource_group_name
  source_address_prefix       = "*"
  source_port_range           = "*"
}

# add data disk
resource "azurerm_managed_disk" "data_disk" {
  create_option        = "Empty"
  location             = var.location
  name                 = "data-disk-${var.prefix}-0"
  resource_group_name  = var.resource_group_name
  storage_account_type = "Premium_LRS"
  disk_size_gb         = 512
  depends_on = [
    azurerm_resource_group.rg,
  ]
}

# vm
resource "azurerm_linux_virtual_machine" "vm" {
  admin_username        = var.username
  location              = var.location
  name                  = "vm-${var.prefix}"
  network_interface_ids = [azurerm_network_interface.nic.id]
  resource_group_name   = var.resource_group_name
  size                  = "Standard_E4s_v3"

  # Generate SSH key
  admin_ssh_key {
    public_key = azapi_resource_action.ssh_public_key_gen.output.publicKey
    username   = var.username
  }

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Premium_LRS"
  }

  source_image_reference {
    offer     = "GitHub-Enterprise"
    publisher = "GitHub"
    sku       = "github-enterprise-gen2"
    version   = "latest"
  }
}

resource "azurerm_virtual_machine_data_disk_attachment" "diskattach" {
  caching            = "ReadWrite"
  lun                = 0
  managed_disk_id    = azurerm_managed_disk.data_disk.id
  virtual_machine_id = azurerm_linux_virtual_machine.vm.id
}

GHESの初期セットアップ

デプロイしたVMのホスト名にアクセス
(立ち上がりまでちょっと待つ)

image.png

ラインセンスファイルをアップロードして管理サイト用のパスワードを入力

image.png

「Set UP a new Instance」 を選択し、初期セットアップ画面に進み、まずはデフォルトのままセットアップを完了させる。

image.png

これまたしばらく待つ。

image.png

完了し、Enterpriseオーナーの作成画面が出てきたら、アカウントを作成する。

このアカウントは認証方法をSAMLに変更後も、予備的にID・パスワードでログインできるようにしておくためのもの。そのため、SAML認証に使用しないアドレス、ユーザー名にしておくこと。

ログインして、右上の 🚀 > Management Console > 管理サイト画面へ移動しておく。

Entra ID 側の設定

以下のドキュメントを参考に進める。

Entra ID > Enterpriseアプリケーション > 新しいアプリケーション > GitHub Enterprise Server

image.png

  1. GitHub Enterprise Server アプリケーション設定に移動

  2. 左側のサイドバーの [シングル サインオン] をクリックし、[SAML] を選択

  3. [基本的な SAML 構成] セクションで、[編集] をクリックし、以下を追加

    • 識別子: https://<HOSTNAME>.com
    • 応答 URL: https://<HOSTNAME>.com/saml/consume
    • サインオン URL: https://<HOSTNAME>.com/sso
  4. [属性とクレーム] セクションで [編集] をクリックし、次のクレームを追加

    • full_name
      • Join (user.givenname, " ", user.surname)
    • emails
      • user.userprincipalname
    • administrator
      • 要求条件:
        • ユーザータイプ: メンバー
        • グループ: グループを指定
        • ソース: 属性
        • 値: true
        • (例) Adminグループに属しているメンバーはEnterpriseオーナー & 管理サイト権限が付くようにする。SAML属性の詳細はこちらを参照

5.[SAML 認定資格証] セクションで、SAML認定資格証 (Base64) をダウンロード
6. [GitHub Enterprise Serverの設定] セクションで、ログインURLMicrosoft Entra識別子をコピーしておく
7. 左側サイドバーの [ユーザーとグループ] からユーザーを割り当てる

GHES 側の設定変更

ここからは下記ドキュメントを参考に。

管理サイト画面に戻り、Atutenticationセクションで [SAML] を選択。

  • [Allow built-in authentication] (組み込み認証を許可)
    • チェックをいれる
      • シングルサインオンだけでなく、ID, パスワードでも入れるようにしておく
  • Single sign-on URL
    • 先ほどコピーしたログインURL
  • Issuer
    • Microsoft Entra 識別子
  • Verification certificate
    • ダウンロードしたSAML認定資格証をアップロード
  • User attributes
    • full_name
    • emails
    • public_keys
    • gpg_keys

設定後は以下のようになる。

image.png

ここまで設定できたら、保存して更新をかけてしばらく待つ。

完了すると、ログイン画面が以下の様になり、SSOできるようになる。
カスタム属性のマッピングができていれば、管理者権限になってるはず。

image.png

「Sign in with username and password」のリンクが画面下部に表示されているため、うまくいかない場合は組み込みのオーナーアカウントのIDとパスワードでログインして設定を確認できる。

さいごに (というか備考)

IDPでユーザーを割り当て、または割り当て解除するときに、IDPはGHESと自動的に通信していない。ユーザーが初めてGHESにアクセスし、IDPを介して認証し、サインインしたときに、GHESは SAML Just-in-Time (JIT) プロビジョニングを使用してユーザー アカウントを作成している。

このJITプロビジョニングでは、IDPからユーザーを削除した場合、GHESインスタンス上のユーザーアカウントを手動で停止する必要がある。

これはめんどくさい。
そこで、自動的にプロビジョニングされるよう、SCIMを設定できる。

が…GHESでこの機能はまだベータ版のよう。

手順通りにやってみようとしたが、個人アクセストークン(Classic) のスコープにscim:enterpriseが見当たらなかった。(なんで?😱)

image.png

これに関しては、もう少し調べてから、また今度やってみる事にする。

1
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
1
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?