ドメイン参加(Linux)
Linux クライアントを作成したドメインに参加させます。
端末は、CentOS8で。
必要なパッケージをインストールします。
# dnf -y install realmd \ sssd \ oddjob \ oddjob-mkhomedir \ adcli \ samba-common-tools \ krb5-workstation
ドメインが検索できるかチェック。
# realm discover LAB.LOCAL lab.local type: kerberos realm-name: LAB.LOCAL domain-name: lab.local configured: no server-software: active-directory client-software: sssd required-package: oddjob required-package: oddjob-mkhomedir required-package: sssd required-package: adcli required-package: samba-common-tools
ドメイン参加。Administrator で参加を実行。
# realm join -U Administrator LAB.LOCAL Administrator に対するパスワード:
# id Administrator@lab.local uid=1571000500(administrator@lab.local) gid=1571000513(domain users@lab.local) groups=1571000513(domain users@lab.local),1571000519(enterprise admins@lab.local),1571000512(domain admins@lab.local),1571000518(schema admins@lab.local),1571000520(group policy creator owners@lab.local),1571000572(denied rodc password replication group@lab.local)
ログインIDにドメイン名省略(use_fully_qualified_names を False)、/homeに作られるホームディレクトリもドメイン名省略する(fallback_homedir を /home/%u)ように設定を修正。
# vi /etc/sssd/sssd.conf # cat /etc/sssd/sssd.conf [sssd] domains = lab.local config_file_version = 2 services = nss, pam [domain/lab.local] ad_domain = lab.local krb5_realm = LAB.LOCAL realmd_tags = manages-system joined-with-adcli cache_credentials = True id_provider = ad krb5_store_password_if_offline = True default_shell = /bin/bash ldap_id_mapping = True use_fully_qualified_names = False fallback_homedir = /home/%u access_provider = ad
# systemctl restart sssd
ドメインのユーザで、sudo コマンドを使えるように細工する。
方法としては、ドメインに sudo を利用できるグループを作成し、ユーザを所属させる。
Linuxには、wheelグループがあるが、作成したグループの ID が異なるため、wheel グループを作成して所属しても、sudo コマンドは利用できない。
そこで、visudo コマンドを実行し、作成したグループを追記して、利用できるようにする。
以下のコマンドを dc.lab.local で実行して、グループ作成
# samba-tool group add "sudousers" Added group sudousers
以下のコマンドを Linuxクライアントで実行して、sudo グループ 設定を実施。
# echo "%sudousers ALL=(ALL) ALL" | EDITOR='tee -a' visudo >/dev/null
ドメイン参加(Windows)
Windows クライアントを作成したドメインに参加させます。
コンピュータ名が登録されるため、ドメイン参加前に端末のコンピュータ名を変更しておきます。
- 「システムのプロパティ」の「変更」ボタンをクリック
- "ドメイン" を選択
- ドメイン名 "lab.local" を入力して「OK」ボタンをクリック (※この際、"lab.local" が検索できる必要があります。DNS サーバのアドレスを DC に設定しておきます。)
- IDは、"LAB\Administrator"。パスワードは、ドメイン作成時に設定したパスワードを入力します。
- 成功した場合、ようこそのメッセージボックスがでるので、「OK」ボタンをクリック。
- 設定画面を「OK」ボタンをクリックして閉じて、端末再起動
ログイン画面が出たら、"LAB\Administrator"で、パスワードは、ドメイン作成時に設定したパスワードを入力してログイン。
これで、ドメイン参加は完了。
ドメインのユーザ管理とかを楽にするために、以下の GUI ツールをインストールします。
インストール方法とかは、サイトのページを参照してください。
インストール後、スタートメニュー -「Windows 管理ツール」-「Active Directory ユーザーとコンピュータ」 から、ユーザーの追加とか行えます。
RSAT(Remote Server Administration Tools for Windows 10)
なお、通常作ったユーザだと、10台までコンピュータの参加が可能なので、上限を撤廃する必要があるみたい。
ADSI エディター を使って、ms-DS-MachineAccountQuota の値 10 を 0 に変更する。
参考:Windows Server 2016に、ドメイン参加できない(コンピュータが登録されずエラー) (Qiita)
また追加したユーザは、いろいろ制限があるので、設定をする。
- 端末の管理者権限がない
端末の設定など不便になるので、管理者権限を持つように設定。
→ グループポリシー を使って、Domain Users を Administrators グループに所属
参考:ActiveDirectory ドメイン参加ユーザにローカルPC管理権限を与える (sys-guard.com) - ドメインの参加権限がない
→ "Account Operators" グループに所属 - ドメインの設定を変更することを可能にする
→ "Domain Admins" グループに所属
DC(Domain Controller)作成
DC(Domain Controller) は、最近だとSamba4でできるみたい。
なので、DC 用の仮想マシンを CentOS8 + Samba4 で、構築してみる。
仮想マシンを作って、CentOS8 をインストール(最小構成のインストールで、最低限のセットアップ)。
ホスト名やIP、ゲートウェイなどは、構想図に合わせて、設定を実施。
CentOS8 からは yum から dnf になっているようなので、dnf でパッケージの更新。
# dnf -y upgrade
ログイン時、Failed to set locale, defaulting to C
のようなエラーが出る場合、言語パックが入っていないためにでるみたい。
日本語でセットアップして、コンソールが英語のみの場合、英語の言語パックをインストールすればOK
# dnf -y install langpacks-en
NTP で時刻合わせするために、CentOS8 の NTP クライアントの chrony をインストール。
NTP の個別設定が必要な場合は、chrony.conf を編集してください。
# dnf -y install chrony # vi /etc/chrony.conf # systemctl restart chronyd # systemctl status chronyd # chronyc sources
Samba4 インストールして、DC を構築します。
CentOS8 の Samba パッケージには、必要なコマンドとかがないので、ソースからビルドする必要があります。
ビルド準備として、ビルドに必要なパッケージをインストール。
参考: Samba Wiki
# dnf -y install bind-utils # dnf -y install dnf-plugins-core # dnf -y install epel-release # dnf -y config-manager --set-enabled PowerTools # dnf -y update # dnf -y install docbook-style-xsl gcc gdb gnutls-devel gpgme-devel jansson-devel \ keyutils-libs-devel krb5-workstation libacl-devel libaio-devel \ libarchive-devel libattr-devel libblkid-devel libtasn1 libtasn1-tools \ libxml2-devel libxslt lmdb-devel openldap-devel pam-devel perl \ perl-ExtUtils-MakeMaker perl-Parse-Yapp popt-devel python3-cryptography \ python3-dns python3-gpg python36-devel readline-devel rpcgen systemd-devel \ tar zlib-devel cups-devel # dnf clean all
Samba ソースをダウンロードして、ビルド。
configure にオプションを追加して、systemd で制御する service ファイルを作成するようにします。
また、service ファイルが systemd が認識する場所に格納されないので、コピーして、自動起動できるようにしておく。
(configure のオプションで解決できると思うが。。。)
有効は、smb.conf がないので、エラーになるから後で。
# curl -OL https://download.samba.org/pub/samba/samba-latest.tar.gz # tar zxvf samba-latest.tar.gz # cd samba-4.12.0/ # ./configure --with-systemd --systemd-install-services # make # make install # cp /usr/local/samba/lib/systemd/system/smb.service /usr/lib/systemd/system/ # cp /usr/local/samba/lib/systemd/system/samba.service /usr/lib/systemd/system/ # cp /usr/local/samba/lib/systemd/system/nmb.service /usr/lib/systemd/system/ # cp /usr/local/samba/lib/systemd/system/winbind.service /usr/lib/systemd/system/ # systemctl daemon-reload # systemctl list-unit-files --type=service
.bash_profile の PATH に、Samba のパスを設定。
# vi ~/.bash_profile # cat ~/.bash_profile if [ -f ~/.bashrc ]; then . ~/.bashrc fi PATH=/usr/local/samba/bin:/usr/local/samba/sbin:$PATH:$HOME/bin export PATH # source ~/.bash_profile # samba -V
Firewall に設定を追加して、DC としてのポートを許可する。
# firewall-cmd --add-port={53,88,389,464}/{tcp,udp} --permanent # firewall-cmd --add-port={135,139,445,636}/tcp --permanent # firewall-cmd --add-port=137-138/udp --permanent # firewall-cmd --add-port=1024-5000/tcp --permanent # firewall-cmd --add-port=3268-3269/tcp --permanent # firewall-cmd --add-port=49152-65535/tcp --permanent # firewall-cmd --reload
SELinux を permissive に。
SELinux の設定を正しくすれば、enforcing のままでも動作するはず。
# vi /etc/selinux/config # cat /etc/selinux/config SELINUX=permissive SELINUXTYPE=targeted # reboot
Samba-tool を実行して、ドメイン設定を行う。
基本は、デフォルトで OK なので、Enter で。
# samba-tool domain provision --use-rfc2307 --interactive Realm [LAB.LOCAL]: Domain [LAB]: Server Role (dc, member, standalone) [dc]: DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE) [SAMBA_INTERNAL]: DNS forwarder IP address (write 'none' to disable forwarding) [192.168.50.254]: Administrator password: Retype password:
# systemctl start samba # systemctl enable samba
Samba 動作確認。
# smbclient -L localhost -U% Sharename Type Comment --------- ---- ------- sysvol Disk netlogon Disk IPC$ IPC IPC Service (Samba 4.12.0) SMB1 disabled -- no workgroup available # smbclient //localhost/netlogon -UAdministrator -c 'ls' Enter LAB\Administrator's password: . D 0 Wed Apr 1 21:58:55 2020 .. D 0 Wed Apr 1 21:59:01 2020 52403200 blocks of size 1024. 49538124 blocks available
DC が DNS になるので、DNS 設定を書き換え。
nmtui で、NIC の DNS サーバーを DC サーバーの IP アドレス、ドメイン検索に、"lab.local" を設定する。
また、ルータの DHCP サーバーで配布する DNS サーバーアドレスも、この DC サーバーの IP アドレスに変えておくとよいかも。
# nmtui # reboot # cat /etc/resolv.conf search lab.local nameserver 192.168.200.1
DNS 動作確認
# samba-tool dns zonelist 127.0.0.1 -U Administrator
# host dc.lab.local dc.lab.local has address 192.168.200.1 # host -t SRV _ldap._tcp.lab.local _ldap._tcp.lab.local has SRV record 0 100 389 dc.lab.local. # host -t SRV _kerberos._tcp.lab.local _kerberos._tcp.lab.local has SRV record 0 100 88 dc.lab.local. # host www.yahoo.co.jp www.yahoo.co.jp is an alias for edge12.g.yimg.jp. edge12.g.yimg.jp has address 182.22.28.252
Kerberos 認証関係の設定。
# mv /etc/krb5.conf krb5.conf.org # cp -p /usr/local/samba/private/krb5.conf /etc/krb5.conf # cat /usr/local/samba/private/krb5.conf [libdefaults] default_realm = LAB.LOCAL dns_lookup_realm = false dns_lookup_kdc = true [realms] LAB.LOCAL = { default_domain = lab.local } [domain_realm] dc = LAB.LOCAL # kinit Administrator@LAB.LOCAL Password for Administrator@LAB.LOCAL: Warning: Your password will expire in 41 days on 2020年05月13日 21時59分01秒 # klist Ticket cache: FILE:/tmp/krb5cc_0 Default principal: Administrator@LAB.LOCAL Valid starting Expires Service principal 2020-04-01T22:13:30 2020-04-02T08:13:30 krbtgt/LAB.LOCAL@LAB.LOCAL renew until 2020-04-02T22:13:26
ユーザのパスワードポリシー設定。
複雑性OFF # samba-tool domain passwordsettings set --complexity=off パスワード長 8文字以上 # samba-tool domain passwordsettings set --min-pwd-length=8 パスワード有効期間 無期限 # samba-tool domain passwordsettings set --max-pwd-age=0 # samba-tool domain passwordsettings set --min-pwd-age=0 確認 # samba-tool domain passwordsettings show Password information for domain 'DC=lab,DC=local' Password complexity: off Store plaintext passwords: off Password history length: 24 Minimum password length: 8 Minimum password age (days): 0 Maximum password age (days): 0 Account lockout duration (mins): 30 Account lockout threshold (attempts): 0 Reset account lockout after (mins): 30
ここまでくれば、DC として動くようになっているはず!
ルータ構築
ルータの OS として、Linux とか Windows でも、よいのだが、今回は軽量な vyOS にしてみた。 (VM ホストは、ちょっと豪華な PC なので、なるべく軽く。。。)
ISOの入手や、推奨マシンスペック、インストール手順などは、以下を参考にして実施。
ルータ マシン用意
192.168.200.0/24 のネットワークと、ルータの VM(512MB のメモリと 2GB のストレージ) を用意。
vyOS インストール
まずISO を入手して、CD ブートを実施。
そして、ログイン。(ID, パスワードは、起動画面に明記されている)
次のコマンドを実行し、質問に答えれば、インストールできる。
# install image
終わったら、再起動して、HDD からブートする。
# reboot
設定
日本語配列キーボード設定
キーボードの記号類が正しく入力されないので、キーマップを変更し、正しく入力できるようにする。
Vyatta/VyOSで利用するキーボードを日本語配列キーボードにする を参考にして、変更する。
ファイルを直接編集する場合は、root にならないと保存できない。
以下のコマンドで root になれる。
# sudo -i
以降の設定をする場合は、編集モードに移行して行う。
# configure
# set interfaces ethernet eth0 address 192.168.50.10/24 # set interfaces ethernet eth0 description 'Out-network'
# set interfaces ethernet eth1 address 192.168.200.254/24 # set interfaces ethernet eth1 description 'Inner-network'
# set protocols static route 0.0.0.0/0 next-hop 192.168.50.254 distance 200
system に設定することも可能。 ただし、内側の Network が 2つ以上ある場合、内側同士のルーティングがデフォルトゲートウェイに流れてしまって、うまく動かない可能性あり。 この形式で指定して、distance で正しくルーティングするようにする。
# set protocols static route 192.168.200.0/24 next-hop 192.168.200.253 distance 1
# set service dhcp-server shared-network-name eth1 subnet 192.168.200.0/24 default-router 192.168.200.254 # set service dhcp-server shared-network-name eth1 subnet 192.168.200.0/24 dns-server 192.168.50.254 # set service dhcp-server shared-network-name eth1 subnet 192.168.200.0/24 domain-name lab.local # set service dhcp-server shared-network-name eth1 subnet 192.168.200.0/24 start 192.168.200.128 stop 192.168.200.253
192.168.200.128/24 ~ 192.168.200.253 を DHCP で割り当てる。
# set service dns forwarding cache-size 0 # set service dns forwarding listen-on eth1 # set service dns forwarding system
192.168.200.254 に来た DNS 向け要求を転送
# set service ssh port 22
# set system host-name router.lab.local # set system name-server 192.168.50.254 # set system time-zone Asia/Tokyo
# show
# commit
# save
CI/CD トライ!
CI/CD やってみようかなってことで、自宅マシンに、VM 立てて、そこでやってみよう! せっかくだから、認証とかも、DC(Domain Controller) 立てて一括管理とかチャレンジしてみるか。
って軽い感じで、やってみる!
そんなわけで、ざっくりネットワーク構成考えてみた。
とりあえず、内部なんでルータのセキュリティはなしで!(笑) 相互にお互いのネットワーク行き来できるようにして、いろいろ遊べる環境に!
DC 立てて、各マシンをドメインに参加して、認証をすべて DC で。 GitLab 立てて、コミットすれば、各 GitLab Runner でビルド実行
まずは、ここまでの CI を目標にするか。
(後に ansible 使ってデプロイ、単体テスト実行まで行けたらいいな。。。)
CSV出力
Attributeで遊んでみようと、とりあえずCSVを出力するのをやってみようかと。
ってことで、作ってみた。
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; namespace CSVWriter { public class CsvWriter { #region 内部利用クラス /// <summary> /// CSVヘッダー情報 /// </summary> protected class CsvHeaderInfo { #region Property /// <summary> /// 表示ラベル /// </summary> public string Label { get; set; } /// <summary> /// プロパティ名 /// </summary> public string PropertyName { get; set; } #endregion /// <summary> /// コンストラクタ /// </summary> public CsvHeaderInfo() { } /// <summary> /// コンストラクタ /// </summary> /// <param name="label">表示ラベル</param> public CsvHeaderInfo(string label) { Label = label; PropertyName = label; } } /// <summary> /// CSV形式の文字を生成する /// </summary> protected class CsvDataFormatter : List<string> { /// <summary> /// データを設定する /// </summary> /// <param name="data">データ</param> /// <returns>データを追加した自身</returns> public CsvDataFormatter SetData(IEnumerable<string> data) { this.Clear(); this.AddRange(data); return this; } /// <summary> /// CSVの文字をエスケープする /// </summary> /// <param name="data">エスケープする文字列</param> /// <returns>エスケープした文字列</returns> private string Escape(string data) { if (string.IsNullOrEmpty(data)) { return ""; } int index = data.IndexOfAny(new char[] { '\n', ',', '"' }, 0); if (index != -1) { data = string.Format("\"{0}\"", data.Replace("\"", "\"\"")); } return data; } /// <summary> /// データをカンマ区切り文字列へ変換する /// </summary> /// <returns>カンマ区切り文字列</returns> public override string ToString() { return string.Join(",", this.Select(item => Escape(item))); } } #endregion protected List<CsvHeaderInfo> GetHeaderInfo(Type t) { var tergetHeaders = t.GetProperties().Where(prop => { return prop.GetCustomAttributes(typeof(CsvIgnoreAttribute), false).Count() == 0; }).Select(prop => { CsvHeaderInfo data = new CsvHeaderInfo(prop.Name); CsvHeaderAttribute attr = Attribute.GetCustomAttribute(prop, typeof(CsvHeaderAttribute)) as CsvHeaderAttribute; if (attr != null) { data.Label = attr.Label; } return data; }); List<CsvHeaderInfo> ret = new List<CsvHeaderInfo>(); ret.AddRange(tergetHeaders); return ret; } /// <summary> /// CSVファイルに出力する /// </summary> /// <param name="fileName">出力するファイル名</param> /// <param name="data">出力するデータ</param> /// <param name="outLabel">ラベルを出力するかどうかのフラグ</param> public void Write(string fileName, object[] data, bool outLabel = true) { StreamWriter sw = null; try { sw = new StreamWriter(fileName, false, Encoding.GetEncoding("shift_jis")); Write(sw, data, outLabel); } finally { if (sw != null) { sw.Close(); } } } /// <summary> /// ストリームにCSVデータを出力する /// </summary> /// <param name="fileName">出力するストリーム</param> /// <param name="data">出力するデータ</param> /// <param name="outLabel">ラベルを出力するかどうかのフラグ</param> public void Write(StreamWriter sw, object[] data, bool outLabel = true) { Type t = data[0].GetType(); List<CsvHeaderInfo> header = GetHeaderInfo(t); CsvDataFormatter formatter = new CsvDataFormatter(); if (outLabel) { // Output Label. string headerData = formatter.SetData(header.Select(p => p.Label)).ToString(); if (!string.IsNullOrEmpty(headerData)) { sw.WriteLine(headerData); } } // Output Data foreach (object one in data) { string oneLine = formatter.SetData(header.Select(p => { PropertyInfo prop = t.GetProperty(p.PropertyName); if (prop != null) { return prop.GetValue(one, null).ToString(); } return ""; })).ToString(); if (!string.IsNullOrEmpty(oneLine)) { sw.WriteLine(oneLine); } } } } #region Attribute /// <summary> /// CSVとして出力する際の属性 /// </summary> [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)] public class CsvHeaderAttribute : Attribute { #region Property /// <summary> /// ラベル情報 /// </summary> public string Label { get; set; } #endregion /// <summary> /// コンストラクタ /// </summary> /// <param name="label">ラベル情報</param> public CsvHeaderAttribute(string label) { Label = label; } } /// <summary> /// CSVとして出力する際の無視属性 /// </summary> [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)] public class CsvIgnoreAttribute : Attribute { } #endregion }
出力するデータのクラスを作成しておいて、作成したクラスの配列を渡せば、プロパティの内容をそのまま出力する様になっています。
行頭にラベルをありつけることもでき、CsvHeaderAttribute属性をプロパティに設定すれば、その値がラベルとして出力されます。
また、CsvIgnoreAttribute属性をプロパティに設定すれば出力されないように制御できます。
使用例はこんな感じ。
class FruitData { [CsvHeader("果物の名前")] public string Name { get; set; } [CsvHeader("値段")] public int Price { get; set; } [CsvIgnore()] public string Shop { get; set; } } class Program { static void Main(string[] args) { FruitData[] data = new[] { new FruitData() { Name = "リンゴ", Price = 120, Shop = "A商店" }, new FruitData() { Name = "オレンジ", Price = 100, Shop = "B商店" }, new FruitData() { Name = "リンゴ", Price = 150, Shop = "B商店" } }; CsvWriter writer = new CsvWriter(); writer.Write("Shopping.csv", data); } }
出力されたCSVはこんな感じ。
果物の名前,値段 リンゴ,120 オレンジ,100 リンゴ,150
DebuggerHidden属性
デバッガの抑制を行います。
動作的には、DebuggerStepThroughAttributeと同じように動作します。
ただし、DebuggerStepThroughAttributeは、メソッド内にブレークポイントを設定すれば止まりますが、DebuggerHiddenAttributeの場合は、設定しても止まりません。
[DebuggerHidden] static void Print() { Debug.WriteLine("Print Method"); }