このブログを検索

2020/10/11

ボクボク証明書(その1)

ボクボク証明書とは、自己署名証明書ではあるがオレオレ証明書よりはすこしマシな証明書のことである。

まず、証明書について基本的なことを確認しておく。

ここでいう証明書とは、インターネットのサーバにブラウザでアクセスするときにサーバが正統(正当?)であることを証明するための証明書のことである。

昔のインターネットでは、サーバ証明書などというものは必要なかったが、次第に利用されることが増え、最近では証明書を提示しないサーバとの通信は「安全でない通信」「保護されていない通信」などと表示されるようになった。

証明書を提示する通信は、「HTTPS」と呼ばれるプロトコルを使用する。

「HTTPS」とは、SecureなHTTPという意味である。

例えばオンラインショッピングでクレジットカード情報や住所等を入力するサイトである場合、そのサーバとの通信を他者に知られないように暗号化する必要がある。

HTTPSの通信が暗号化された通信であるという認識はほとんどの人が持っているだろうが、当たり前のことであるが、暗号化するには通信相手が本当に自分の通信したい相手なのかを確認する必要がある。

暗号化しているから安心だ、と、有名サイトを装った偽のサイト、いわゆるフィッシングサイトに接続してしまわないようにする必要である。

そのためにHTTPS通信ではサーバが証明書を提示し、クライアントはその証明書によりサーバがホンモノであることを確認して初めて「保護された通信」がおこなわれるのである。

しかし皆さんはおそらく『証明書がどうこうというのは知っているけど別にそれをいちいち確認してないけど』と思っているだろう。私もそうである。

サーバーがホンモノかどうか、つまり証明書が信頼できるかどうかは、ブラウザが判断している。

何をもって正統だと判断するか。

それは、証明書の発行者を見るのである。

そして、その発行者が信頼できる発行者であれば、その証明書は信頼できるとみなすのである。

ではどうやって発行者が信頼できるとみなすのか。

それは、「信頼できる証明書発行者リスト」というものを参照するのである。

それはあなたのパソコンの中にもある。


が、疑い深い人は、まだ安心できず「その『信頼できる証明書発行者リスト』が信頼できるとどうやって判断するのか、と思うだろう。


そしてここでようやくあなたの出番である。

あなたが使うパソコンにある、「信頼できる証明書発行者リスト」が信頼できるものであると、あなたが判断すれば、それを参照しているブラウザの判断も正しいとみなす。

あなたはそんなリストを登録した覚えはない、見たことすらない、というかもしれないが、

おそらくHTTPS通信というものはそういう建前で動いている。

自分で登録していなければ、ブラウザインストール時あるいはOSインストール時(インストール済みPCであれば購入時)に登録されるリストを信頼すると、あなたが受け入れていることになっているのだ。


前置きが長くなったが、ボクボク証明書を説明するために必要な前提条件である。

さて、「オレオレ証明書」とは、「発行者が自分自身である証明書」のことである。

今回発行する証明書も自分自身で署名しているのだが、一応「信頼」してもらえる証明書になる。

そのカラクリは、先ほど説明した「信頼できる証明書発行者リスト」にある。

もうお気づきであろう。

ボクボク証明書というのは、「信頼できる証明書発行者リスト」に、あなたという証明書発行者を追加してもらうことにより、信頼できる証明書としてもらうのである。


御託はこれくらいにして、以下、実践。

以前やった時とはいろいろ変わっていたので改めて手順を書く。


apacheとopensslのバージョンは以下


# httpd -v

Server version: Apache/2.4.37 (centos)

Server built:   Jun  8 2020 20:14:33


# openssl version

OpenSSL 1.1.1c FIPS  28 May 2019


手順の流れ

1.CA証明書作成

 1-1. CAの秘密鍵作成

 1-2. CA証明書のCSR発行

 1-3. CA証明書のCSRにCAの秘密鍵を用いて署名


2.WEBサーバの証明書作成

 2-1. WEBサーバの秘密鍵を作成

 2-2. WEBサーバ証明書のCSRを発行

 2-3. WEBサーバ証明書にCAの秘密鍵を用いて署名

 2-4. Apacheの設定ファイルで発行した証明書と秘密鍵の場所を指定


3. 動作確認

 3-1. CA証明書をクライアントにインストール

 3-2. サイトにアクセス


手順

以下手順では必要に応じてカレントディレクトリを移動しているので、ファイル名を指定する箇所のパス名は実施する状況に応じて変更が必要な場合があります。

ドメイン名、ホスト名等は必要に応じて変更しているので参考程度にしてください。


1.CA証明書作成

1.1 CAの秘密鍵作成

# openssl genrsa -aes256 -out ./cakey.pem 2048

Generating RSA private key, 2048 bit long modulus (2 primes)

...................................................................................+++++

..........................................+++++

e is 65537 (0x010001)

Enter pass phrase for ./cakey.pem:(パスフレーズを入力)

Verifying - Enter pass phrase for ./cakey.pem:(パスフレーズを入力)


1.2 CA証明書のCSR発行

openssl req -new -key ./cakey.pem -out ./cacert.csr


※-keyでCAの秘密鍵ファイルを、-outで発行するCSRファイルを指定する。


# openssl req -new -key ./cakey.pem -out ./cacert.csr

Enter pass phrase for ./cakey.pem:(CAの秘密鍵のパスフレーズを入力)

You are about to be asked to enter information that will be incorporated

into your certificate request.

What you are about to enter is what is called a Distinguished Name or a DN.

There are quite a few fields but you can leave some blank

For some fields there will be a default value,

If you enter '.', the field will be left blank.

-----

Country Name (2 letter code) [XX]:JP

State or Province Name (full name) []:Tokyo

Locality Name (eg, city) [Default City]:Shinjuku

Organization Name (eg, company) [Default Company Ltd]:

Organizational Unit Name (eg, section) []:

Common Name (eg, your name or your server's hostname) []:ca.monqy.net

Email Address []:


Please enter the following 'extra' attributes

to be sent with your certificate request

A challenge password []:

An optional company name []:

最後のチャレンジパスワードは書いてあるようにextraの設定なので入れなくてもよい。


1.2 CA証明書のCSRにCAの秘密鍵を用いて署名

# openssl x509 -days 825 -in ./cacert.csr -req -signkey ./cakey.pem -out ./cacert.pem

Signature ok

subject=C = JP, ST = Tokyo, L = Shinjuku, O = Default Company Ltd, CN = ca.monkey.net

Getting Private key

Enter pass phrase for ./cakey.pem:(CAの秘密鍵のパスフレーズ)


2.WEBサーバー証明書作成

以下が前提である。

opensslの設定ファイルの場所: /etc/pki/tls/openssl.cnf

openssl.cnfの設定

default_ca = CA_default
dir = /etc/pki/CA     

CAの秘密鍵の保存ディレクトリ: /etc/pki/CA/private/cakey.pem

WEBサーバ証明書等の保存ディレクトリ: /etc/pki/SSL


2.1 WEBサーバーの秘密鍵作成

# openssl genrsa -aes256 -out ./serverkey.pem 2048

Generating RSA private key, 2048 bit long modulus (2 primes)
............................................................................+++++
................+++++
e is 65537 (0x010001)
Enter pass phrase for ./server.key:(パスフレーズを入力)
Verifying - Enter pass phrase for ./server.key:(確認のためパスフレーズを再入力)


これはCAの秘密鍵を作ったときと全く同じことをしている。


2.2 WEBサーバーのCSR作成

[root@jesus SSL]# openssl req -new -key ./server.key -out ./server.csr

Enter pass phrase for ./server.key:(WEBサーバーの秘密鍵のパスフレーズ)

140637670844224:error:28078065:UI routines:UI_set_result_ex:result too small:crypto/ui/ui_lib.c:903:You must type in 4 to 1023 characters

Enter pass phrase for ./server.key:(WEBサーバーの秘密鍵のパスフレーズ)

You are about to be asked to enter information that will be incorporated

into your certificate request.

What you are about to enter is what is called a Distinguished Name or a DN.

There are quite a few fields but you can leave some blank

For some fields there will be a default value,

If you enter '.', the field will be left blank.

-----

Country Name (2 letter code) [XX]:JP

State or Province Name (full name) []:Tokyo

Locality Name (eg, city) [Default City]:Xxxxx

Organization Name (eg, company) [Default Company Ltd]:

Organizational Unit Name (eg, section) []:

Common Name (eg, your name or your server's hostname) []:*.monqy.net

Email Address []:


Please enter the following 'extra' attributes

to be sent with your certificate request

A challenge password []:

An optional company name []:


これもCAと同じことをしている。指定する秘密鍵がWEBサーバのものであることと、Common Nameが違うだけである。

Common Nameは *.monqy.net としているが、
こうしておくとホスト名が変わっても同じ証明書が使えるそうである。

たとえば、www.monqy.net でも www2.monqy.net でも、というように。


Common NameはCNと略され、サーバにアクセスするときのfqdnである必要がある。
fqdnとCNが異なっていると正しい証明書とみなされない。
さらに、今はCNがfqdnと一致しているだけでなく、もう一か所別の設定でもfqdnを記述する必要がある。
(後述)



---------------------------------------------
(追記)
openssl.confの設定により場所が異なるが、下記ファイルが必要

index.txt
serial.txt

今やったのだと /etc/pki/CA

index.txt はあれば中身がなくてもよいが
serial.txt は4桁の数字が書いてある必要があるらしい
今やったのは 0001 で成功した。
1  だけだと失敗する
-----------------------------------------

2.3 WEBサーバ証明書にCAの秘密鍵を用いて署名

以下のように書いたテキストを作る(san.txtとする)

subjectAltName=DNS:www.monqy.net

そして、署名の際に最後に以下のようなオプションを付けて署名する。
-extfile san.txt


openssl ca -config /etc/pki/tls/openssl.cnf -in /etc/pki/SSL/server.csr -keyfile /etc
/pki/CA/private/cakey.pem -cert /etc/pki/CA/cacert.pem -out server.crt -extfile san.txt
Using configuration from /etc/pki/tls/openssl.cnf
Enter pass phrase for /etc/pki/CA/private/cakey.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 0 (0x0)
        Validity
            Not Before: Oct 11 01:25:48 2020 GMT
            Not After : Oct  9 01:25:48 2030 GMT
        Subject:
            countryName               = JP
            stateOrProvinceName       = Tokyo
            organizationName          = Default Company Ltd
            commonName                = *.monkey.net
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                31:D5:35:FC:B3:F9:6F:F0:29:84:47:4A:7C:B5:CB:8F:67:D0:26:2A
            X509v3 Authority Key Identifier:
                DirName:/C=JP/ST=Tokyo/O=Default Company Ltd/CN=*.monkey.net
                serial:5C:C5:FC:36:68:EF:F0:0A:CF:AF:DE:75:E5:1E:E6:4B:83:68:2C:38

Certificate is to be certified until Oct  9 01:25:48 2030 GMT (3650 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

確認が2回来るのでyを入力する。
有効期間が10年間になっているが、デフォルトは1年である。

openssl.cnfの下記の設定で変更できる。
default_days   = 3650


-extfileで指定した「subjectAltName」であるが、最近はCNだけではなくこちらも見るようである。

これをやらないとchromeでは NET::ERR_CERT_COMMON_NAME_INVALID のエラーになる。

(参考)
https://www.pistolfly.com/weblog/2017/06/chrome%E3%81%A7-neterr_cert_common_name_invalid-%E3%82%A8%E3%83%A9%E3%83%BC.html

ここの記述によると、最近のchromeはcnは観ずにSANのみを見るらしい。


2-4. Apacheの設定ファイルで発行した証明書と秘密鍵の場所を指定

/etc/httpd/conf.d/ssl.conf


ServerName www.monqy.net:443

SSLProtocol all -SSLv3

SSLCertificateFile /etc/pki/SSL/server.crt

SSLCertificateKeyFile /etc/pki/SSL/server.key


ServerNameのfqdnがあっていることも確認

httpd再起動




3.動作確認

3.1 CA証明書のインストール

CA証明書をクライアント(Windows10)にダウンロードし、拡張子 .crt にして保存する。

/etc/pki/CA/cacert.pem
ca.crt

ca.crtをダブルクリックすると証明書の内容が表示される。
「証明書のインストール」をクリックし、「ローカルコンピューター」「信頼されたルート証明機関」にインストールする。

※ここでなくてもよいかもしれないが、ここにインストールして確認した。

3.2 ブラウザでアクセス

Chrome, Edge, FireFoxでかくに...!!!

FireFoxでは信頼されない!!

「誰かがこのサイトに偽装しようとしている可能性があります。続行しないでください。
 
ウェブサイトは証明書で同一性を証明します。証明書の発行者が不明、証明書が自己署名、またはサーバーが正しい中間証明書を送信していないため、Firefox は www.monqy.net を信頼しません。
 
エラーコード: SEC_ERROR_UNKNOWN_ISSUER」



chromeとedgeで問題なかったので、念のためFireFoxでも見ておくかと思ったらこのザマだ。


エラーメッセージについて調べると、単にCA証明書が見つからないというだけのようだ。
FireFoxは例の「信頼されたルート証明機関」を見ていないのか?

「オプション」「プライバシーとセキュリティ」「証明書」「証明書を表示...」
「認証局証明書」に認証局証明書の一覧がある。

今回作成してインストールした証明書はない。
FireFoxは独自の場所に信頼するCA証明書のリストを持っているようだ。

ここに今回作成したCA証明書をインポートしてから接続すると、
ブラウザにエラーは表示されなくなった。

しかし、証明書を表示すると「Mozillaが承認していない発行者の証明書で検証された接続です」と表示される。

これも消したいな...




2020/08/08

Raspberry Pi 3 Model A+

 コンソールはデフォルトで無効でアクセスできない

sdカードにOSを焼いた後、ssh と wpa_supplicant.conf を置く方法を使う。

OSは

2020-05-27-raspios-buster-lite-armhf.img


コンソールを有効にしてつないだがゴミが延々と表示される。

こないだzeroにつないだ時も同様だった。

ログインプロンプトが見えるのだが...

ケーブルがイカれたか?


2020/07/22

順列と組み合わせ

Permutationは「順列」、Combinationは「組み合わせ」と訳される。

教科書では、

「a,b,c,dの4個の文字の中から、異なる3個をとって1列に並べる」
「7人の生徒から3人を選んで1列に並べる」
「男子3人、女子2人が1列に並ぶとき女子2人が隣り合うような並び方は何通りあるか」
「5人を、2つの部屋A, Bに入れる方法は何通りあるか、ただし、一人も入らない部屋があってもよいものとする」

「12色の鉛筆から5色の色鉛筆を選ぶ方法」
「正六角形の6個の頂点のうちの3個を頂点とする三角形の個数」
「男子10人、女子6人の中から、5人を選ぶとき、(1)男子3人と女子2人を選ぶ(2)男子が少なくとも1人含まれる 選び方はそれぞれ何通りか」

などという例が使われている。

これらについて、permutation, combination,和の法則、積の法則を使って計算する方法が説明される。

私は授業でそれをぼーっと聞きながら、『しらみつぶしに数えなくても、数えきれないような場合でも、こうやって計算できて便利なんだな』と思いながらも、『どんな時にこんな計算が必要になるのだろう』という疑問というか、無力感に苛まれて、真面目に勉強する気にならなかった。

それは順列組み合わせだけでなく、ベクトル、三角関数、指数関数、対数関数、数列、微分積分すべてに言えることである。

高校生になると数学はどんどん複雑になり、直観やしらみつぶしが通用しなくなってくる。


今、「ワンペア問題」を考えていて、PermutationとCombinationについてあらためて考えなおしている。

「a,b,c の3つの文字から2つを選んで並べる組み合わせはいくつあるか」

a,b
a,c
b,c

3つ。

では、a,b,c,dの4つから2つだったら?

a,b
a,c
a,d
b,c
b,d
c,d

6つ。

高校生だった私は、この手のことを考える必要性に悩まされた。

当時は単なる怠け心だとしか思わず、勉強とは、生きるとは、成長するとはそういう疑問を持たないことだと言い聞かせるものの、結局それに打ち勝てなかった。

そのころに、「人間が生きる目的は何か」ということを考えるようになったのも、哲学に興味を持ち始めたのも、無意味に思えることを考える必要性がわからなかったからなのだと、今になってみるとわかる。

「数学なんか生活に必要ない」なんて言うと、いかにもバカで怠惰なろくでもない奴のようで、私自身もそういう考えは嫌いで、勉強とは学問とは生活に必要だからするようなものではないと思ってはいたが、果たして必死に「勉強」している級友たちがそんな高尚なことを考えていたとは思えない。

むしろ彼らはそんなことを考えもせず、勉強というのは受験であり受験とは生存競争であり椅子取りゲームであると割り切って、四の五の言わずに定番問題集を解いて過去問を解きまくって、パターンを覚えて、どんな難しい問題もすべて何かのパターンにあてはめれば解ける、という生き方を選んでいただけではなかったのか。

その証拠に彼らは、授業でキェルケゴールやカントやベーコンが出てきても、国語の教科書に舞姫や伊勢物語や源氏物語や史記や論語が載っていても、その本の一部が載っているだけなのに、それらに興味を示さず、「こんなの試験に出ない」と言って、授業を聞きながら受験用の参考書の問題集を解いたりしていた。

わたしが「a, b, c をならべる組み合わせはいくつあるか」ということに興味を示さないことと、彼らが「絶望とは死に至る病である」ということに興味を示さないことにどんな違いがあるのか。どちらが怠惰だったのか。

2020/07/20

全部交換する(やり直し)

全部交換の場合は簡単じゃないかな。

下記のような残りのカードがあって、

C1-2,C1-3,C1-4
C2-2,C2-3,C2-4
....
C5-2,C5-3,C5-4

C6-1,C6-2,C6-3,C6-4
...
C13-1,C13-2,C13-3,C13-4



そこから5枚を選んでワンペアができればよくて、


手札は考慮しなくてよいから、


5種類については3パターン、

8種類については6パターン、


で、5枚選ぶので、


5*3*combin(45,3) + 8*6*combin(45,3) = 893970


確率は、893970 / combin(47,5) = 0.582794



1枚交換: 0.255

2枚交換: 0.408
3枚交換: 0.385
4枚交換: 0.772 ☆
5枚交換: 0.583

というわけで、4枚交換が最善という結果になった。



ちなみに前回の計算結果は


1枚交換: 0.255 (同じ)

2枚交換: 0.438
3枚交換: 0.580
4枚交換: 0.706
5枚交換: 0.583 (同じ)


3枚交換の場合の今回の結果がおかしいかな?
また今度。

2枚交換する(やり直し)

手札を C1-1,C2-1,C3-1,C4-1,C5-1 とする。

残りのカードを、

C1-2,C1-3,C1-4
C2-2,C2-3,C2-4
....
C5-2,C5-3,C5-4

C6-1,C6-2,C6-3,C6-4
...
C13-1,C13-2,C13-3,C13-4

とする。

捨てる2枚のカードを、

C1-1,C2-1とする。

手札に残るのは C3-1, C4-1,C5-1


ワンペアができるケースは以下2つの場合のいずれかである。

1.取った2枚のカードの中でペアができる。(ただし、手札とペアになるカードは含まない)
2.取ったカードと手札を合わせてペアができる。


※以下で求める場合の数はすべて順序を考慮しない。
(ことなる順序で同じ組み合わせは同じとみなす)


「1.取った2枚のカードの中でペアができる。(ただし、手札とペアになるカードは含まない)」

取ったカードの中にペアとなる2枚が含まれる場合の数を数える。

ペアとなるのはC1,C2,....C13の13種類のカードの、同じ種類のものが2枚(以上)そろった場合である。

C1~C5は残り3枚なので、その組み合わせは3。

C6~C13は残り4枚なので、その組み合わせは6。

全部足すと、5*3 + 8*6 = 15+ 48 = 63


ゆえに、2枚とったときにその中でワンぺアができる(ただし手札とペアになるカードを含まない)場合の数は

 63 ....①



「2.取ったカードと手札を合わせてペアができる。」

手札に残っている3種類(C3, C4, C5)の、残り3枚のカードのどれかが含まれてさえいればよい。

そのカードとは下記の9枚である。

C3-2, C3-3, C3-4, C4-2, C4-3, C4-4, C5-2, C5-3, C5-4


列挙する。(38)というのは、残り38枚の組み合わせという意味。
手札と合わせてペアになれるカード以外のカードが38枚ある。

これら9枚のうち1枚と、その他38枚のどれかの組み合わせと、
これら9枚のうち2枚を選ぶ組み合わせの合計だから、

38*9 + combin(9,2) =378 ... ②



①に②は含まれない。
②には、「取った2枚の中でペアができる」ものも含まれるが、
①で手札とペアになるカードを除外しているので、①と②が重複することはない。

よって、2枚交換する場合にワンペアができる場合の数(順序を考慮しない)は、

63 + 378 = 441

残り47枚から2枚を選ぶ場合の数(順序を考慮しない)は

combin(47,2) = 1081

求める確率は、 441 / 1081 ≒ 0.408





4枚交換する(やり直し)

自分的には一番オッズの高い4枚交換。

手札を C1-1,C2-1,C3-1,C4-1,C5-1 とする。

残りのカードを、

C1-2,C1-3,C1-4
C2-2,C2-3,C2-4
....
C5-2,C5-3,C5-4

C6-1,C6-2,C6-3,C6-4
...
C13-1,C13-2,C13-3,C13-4

とする。

ここまでは他と同じ


捨てる4枚のカードを、

C1-1,C2-1,C3-1,C4-1とする。

手札に残るのは C5-1


ワンペアができるケースは以下2つの場合のいずれかである。

1.取った4枚のカードの中でペアができる。(ただし、手札とペアになるカードは含まない)
2.取ったカードと手札を合わせてペアができる。


※以下で求める場合の数はすべて順序を考慮しない。
(ことなる順序で同じ組み合わせは同じとみなす)



(条件が違うだけで3枚交換の場合と同じ考え方でいけるだろうか?)



ワンペアができるケースは以下2つの場合のいずれかである。

1.取った4枚のカードの中でペアができる。(ただし、手札とペアになるカードは含まない)
2.取ったカードと手札を合わせてペアができる。

※以下で求める場合の数はすべて順序を考慮しない。
(ことなる順序で同じ組み合わせは同じとみなす)


「1.取った4枚のカードの中でペアができる。(ただし、手札とペアになるカードは含まない)」

取ったカードの中にペアとなる2枚が含まれる場合の数を数える。

ペアとなるのはC1,C2,....C13の13種類のカードの、同じ種類のものが2枚(以上)そろった場合である。

C1~C5は残り3枚なので、その組み合わせは3。

C6~C13は残り4枚なので、その組み合わせは6。

全部足すと、5*3 + 8*6 = 15+ 48 = 63

この63通りというのは、2つの組み合わせなので、
4枚とる組み合わせにこの組み合わせが含まれる場合は、
2枚取った残りの45通り*44通りあるが、
そのうち手札とペアになるカード(3枚)を除くので、42通り*41通りになる。

ゆえに、3枚とったときにその中でワンぺアができる(ただし手札とペアになるカードを含まない)場合の数は

63 * 42 * 41  = 108486 ... ①


「2.取ったカードと手札を合わせてペアができる。」

手札に残っている1種類(C5)の、残り3枚のカードのどれかが含まれてさえいればよい。

そのカードとは下記の3枚である。

C5-2,C5-3,C5-4


列挙する。(44)というのは、47枚残った中から手札とペアを作れる3枚を除いた
残り44枚の組み合わせという意味。

C5-2,combin(44,3)
C5-2,C5-3,combin(44,2)
C5-2,C5-3,C5-4,(44)
C5-2,C5-4,combin(44,2)
-----
combin(44,3)
+
2*combin(44,2)
+
44


C5-3,combin(44,3)
C5-3,C5-4,combin(44,2)
-----
combin(44,3)
+
combin(44,2)


2*combin(44,3) + 3*combin(44,2) + 44 = 29370...②


①に②は含まれない。
②には、「取った3枚の中でペアができる」ものも含まれるが、
①で手札とペアになるカードを除外しているので、①と②が重複することはない。

よって、3枚交換する場合にワンペアができる場合の数(順序を考慮しない)は、

108486 + 29370 = 137856

残り47枚から4枚を選ぶ場合の数(順序を考慮しない)は

combin(47,4) = 178365

求める確率は、 137856/178365 = 0.772887




3枚交換する(やり直し)

手札を C1-1,C2-1,C3-1,C4-1,C5-1 とする。

残りのカードを、

C1-2,C1-3,C1-4
C2-2,C2-3,C2-4
....
C5-2,C5-3,C5-4

C6-1,C6-2,C6-3,C6-4
...
C13-1,C13-2,C13-3,C13-4

とする。

捨てる3枚のカードを、

C1-1,C2-1,C3-1とする。

手札に残るのは C4-1,C5-1


ワンペアができるケースは以下2つの場合のいずれかである。

1.取った3枚のカードの中でペアができる。(ただし、手札とペアになるカードは含まない)
2.取ったカードと手札を合わせてペアができる。


※以下で求める場合の数はすべて順序を考慮しない。
(ことなる順序で同じ組み合わせは同じとみなす)


「1.取った3枚のカードの中でペアができる。(ただし、手札とペアになるカードは含まない)」

取ったカードの中にペアとなる2枚が含まれる場合の数を数える。

ペアとなるのはC1,C2,....C13の13種類のカードの、同じ種類のものが2枚(以上)そろった場合である。

C1~C5は残り3枚なので、その組み合わせは3。

C6~C13は残り4枚なので、その組み合わせは6。

全部足すと、5*3 + 8*6 = 15+ 48 = 63

この63通りというのは、2つの組み合わせなので、
3枚とる組み合わせにこの組み合わせが含まれる場合は、
2枚取った残りの45通りあるが、
そのうち手札とペアになるカードを除くので、39通りになる。

ゆえに、3枚とったときにその中でワンぺアができる(ただし手札とペアになるカードを含まない)場合の数は

39 * 63 = 2457 ....①



「2.取ったカードと手札を合わせてペアができる。」

手札に残っている2種類 C4,C5 の、残り3枚のカードのどれかが含まれてさえいればよい。

そのカードとは下記の6枚である。

C4-2, C4-3, C4-4, C5-2, C5-3, C5-4


列挙する。(41)というのは、残り41枚の組み合わせという意味。
手札と合わせてペアになれるカード以外のカードが41枚ある。

C4-2, * combin(41,2)
C4-2,C4-3,(41)
C4-2,C4-3,C4-4
C4-2,C4-3,C5-2
C4-2,C4-3,C5-3
C4-2,C4-3,C5-4
C4-2,C4-4,(41)
C4-2,C4-4,C5-2
C4-2,C4-4,C5-3
C4-2,C4-4,C5-4
C4-2,C5-2,(41)
C4-2,C5-2,C5-3
C4-2,C5-2,C5-4
C4-2,C5-3,(41)
C4-2,C5-3,C5-4
C4-2,C5-4,(41)
-----
combin(41,2)
+
41*5
+
10



C4-3,combin(41,2)
C4-3,C4-4,(41)
C4-3,C4-4,C5-2
C4-3,C4-4,C5-3
C4-3,C4-4,C5-4
C4-3,C5-2,(41)
C4-3,C5-2,C5-3
C4-3,C5-2,C5-4
C4-3,C5-3,(41)
C4-3,C5-3,C5-4
C4-3,C5-4,(41)
-----
combin(41,2)
+
41*4
+
6


C5-2,combin(41,2)
C5-2,C5-3,(41)
C5-2,C5-3,C5-4
C5-2,C5-4,(41)
-----
combin(41,2)
+
41*2
+
1

C5-3,combin(41,2)
C5-3,C5-4,(41)
-----
combin(41,2)
+
41


combin(41,2)*4 + 41*12 + 17 = 3789 ... ②



①に②は含まれない。
②には、「取った3枚の中でペアができる」ものも含まれるが、
①で手札とペアになるカードを除外しているので、①と②が重複することはない。

よって、3枚交換する場合にワンペアができる場合の数(順序を考慮しない)は、

2457 + 3789 = 6246

残り47枚から3枚を選ぶ場合の数(順序を考慮しない)は

combin(47,3) = 16215

求める確率は、 6246/16215 = 0.385199

全部交換する

COMBIN(3,2)*COMBIN(45,3)+8*COMBIN(4,2)*COMBIN(45,3)

だと思うのだが・・・

分母は combin(47,5)で、確率を出すと

約 0.58279371となる。


というわけで、不完全なのだが、
やっぱり1枚残して4枚交換が一番ワンペアができやすいという結果になった。


私が知りたいのと同じことを某所で質問している人がいて、
答えが一応でていて、やはり1枚残して4枚交換が最善という結論になっているのだが私の計算した確率とはだいぶ違っている。

私の計算にはあまり自信がない。

またヒマを見つけて考え直してみたい。

2020/07/19

3枚交換する(改)

(残した札がペアになる組合せ数)= s1
または
(交換した札のなかでペアになる)= s2
ただし
(ツーペア)=s3 を除く

s1 = 2 * 3 * combin(46,2)
s2 = 11 * combin(4,2) * 45
s3 = 2 * 3 * combin(11,1) * combin(4,2)

分母 b = combin(47,3)
確率は
(s1+s2-s3) / b ≒ 0.580314811


(2020/7/19追記)
再考。

1.取った3枚の中でペアができる
2.手札のどれかがとった3枚のどれかとペアになる

まず「1.取った3枚でペア」
A群(残り3枚、5種類)
 47枚から3枚取り、その中に特定の15枚を2枚以上含む場合。

B群(残り4枚、8種類)
 47枚から3枚取り、その中に特定の32枚を2枚以上含む場合。


A群から。
C1-2,C1-3,(残り45枚)
C1-2,C1-4,(残り45枚)
C1-3,C1-4,(残り45枚)

45パターン * 3 * 5種類
さらに、順番違いを数えると
45 * 3 * 5 * 6 = 45 * 15 * 6 = 45 * 90 = 4050


B群。
C6-1,C6-2,(残り45枚)
C6-1,C6-3,(45)
C6-1,C6-4,(45)
C6-2,C6-3,(45)
C6-2,C6-4,(45)
C6-3,C6-4,(45)

6 * 45 * 8 * 6 = 6 * 45 * 48  = 12960

A群とB群を足して、「1.取った3枚でペア」は、17010 通り


次、「2.手札がペア」

ペアとなれる候補は、
2種類3枚ずつ、合計6枚。

C1-2,C1-3,C1-4
C2-2,C2-3,C2-4

47枚から3枚とって、その中に上記6枚のうちの1枚が含まれる場合。

C1-2,(46),(45)
C1-3,(46),(45)
....

46*45*6 * 6 = 46*45*36 =  74520 通り



そして、1.と2.を同時に満たす場合。
つまり、スリーカードまたはフォーカード。

C1-2,C1-3,(45)
C1-2,C1-4,(45)
C1-3,C1-4,(45)

C2も同様

3 * 45 * 2 * 6 = 135 * 12 = 1620

この中にフォーカードも含まれる
たとえば C1-2,C1-3,C1-4 とか。

「3枚交換でワンペアができる場合の数」は

17010 + 74520 -1620 =  89910


確率は 89910 / Permut(47,3) =  89910 / 97290 ≒ 0.92

こんな高くないよね....

(2020/7/20追記)

もっとシンプルに考えよう。

残り47枚から3枚選ぶ。
ワンペアになれるカードは、手札に残した2種類の、残り3枚ずつの、6枚。

だから、条件に当てはまる場合の数というのは、
「47枚のカードから3枚選んだ時に、特定の6枚が少なくとも1枚含まれる場合」
となる。

「47枚から1枚を選ぶときに、特定の6枚のどれかを選ぶ確率」だったら、
6/47で、簡単だ。

それを3回続ける、と思いかけたが、1枚選んだらそれを除外するから、
さいころを3回振るような場合とは違う。

特定の6枚を選んだ場合を〇、選ばなかった場合をⅩで表すと、

〇〇〇
〇〇×
〇××
×〇〇
×〇×
××〇

のどれでもよい。

〇〇〇 ... 6/47, 5/46, 4/45
〇〇× ... 6/47, 5/46, 41/45
〇×× ... 6/47, 41/46, 40/45
×〇〇 ... 41/47, 6/46, 5/45
×〇× ... 41/47, 6/46, 40/45
××〇 ... 41/47, 40/46, 6/45

上記6パターンは同時には起こらず、(事象A または 事象B)
各パターンの〇×は同時に起こるから、(事象A かつ 事象B)


6/47 * 5/46 * 4/45
+
6/47 * 5/46 * 41/45
+
6/47 * 41/46 * 40/45
+
41/47 * 6/46 * 5/45
+
41/47 * 6/46 * 40/45
+
41/47 * 40/46 * 6/45

じゃないか?

excelで計算したら、0.329941となった。

「3枚交換したときにワンペアができる確率は33%」
って、直感的にも正しそうな気がする。





2020/06/27

Genuino UNO

去年だったかな、千石電商の店頭で500円で売っていたので買ったもの。

調べるとarduino互換機というか、arduinoの商品名が違うだけのもの、みたいなことらしい。

なんで500円なんだろう。

びんぼうでいいのより安い。


LEDを光らせたりブザーを鳴らしたりしてみたがたしかにarduinoと同じだ。

載っているマイコンが違う。


ATMEGA328P-PU と書いてあるのは同じだが、
arduinoは、1602、genuinoは 1532である。

ちなみにびんぼうでいいのは MEGA328P AU 1633

違いは、軽く検索したけどよくわからない。


2020/06/06

さくらのVPSにcentos8をインストールして最初にしたこと

まず、さくらのVPSのコントロールパネルで、「パケットフィルタ」というファイアウォール的な設定があったのでSSH(カスタム)とWEBを開放したのだが、
これって、centosのfirewallのさらに外側の設定だよね?

二重管理になるから嫌だな.... 

でも、開放ポートを設定するようになっているから、
centosのFirewallで設定するようにするなら全開放にするってことか。
それもヤダな....

まあ、あとで考える。


さて、やったこと。

hostnameの変更

hostnamectlコマンド

sshd.confの設定変更
・ポート番号変更
・PermitRootLogin を noに

httpdのインストール(dnfで)


python3は最初から入っているのかと思ったら入ってなかった。
(たしかconohaは入ってた)

python3のインストール(dnfで)

python3とpip3を、それぞれ「python」「pip」で呼べるように変更

・sudo alternatives --config python を実行し、python3を選ぶ
・sudo update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 1

コントロールパネルで、OS再インストールしたら「帯域制限中」と表示されていたのに気づいたのだがいつのまにか消えた。
OSをインストール直後はこうなるのかな。

VPSとのファイル送受信をscpでおこなう

さくらのVPSはcentos7で使っているが、主にpython2系・3系問題がわずらわしいので、centos8に一新したいと思った。

そこでバックアップをとろうとして、なにか簡単な方法はないかと考えた。

コピーするファイルはそれほどのサイズではない、ほとんどがテキストファイルである。


今までは、vsftpdを起動してFTPを使ったり、wgetでダウンロードしたりしていたのだが、scpがシンプルで便利だ。
scp -r -P xxxx MyDirectory hoge@monqy.net:~/share
これで、MyDirectory 配下のファイルをコピーできる。
SSHのポートを変更しているので -P で指定する。

コピー先のフォルダはしかるべきpermissionにする必要がある。
最低必要なpermissionはわからないので 777 にしている。
なので、ファイル受け渡し用のフォルダを作った。

ftpを使うのはダサい。
暗号化されてないからセキュリティ上問題があるとかではなく、
手間をかけるのがダサい。

2020/05/19

FILCOのキーボードFKBN91M

自宅勤務になり外出もしないので一日中家でキーボードを叩くことになった。

いままでキーボードにはなんのこだわりもなく、秋葉原の店頭で売っている500円とかの中古キーボードなどを使っていたが、

なんかキーが歯周病患者の歯のようにグラグラした感じがしているのが気になり、
この際ちょっといいのを買ってみようと思って、FILCOのFKBN91Mというのを買った。

13000円くらいだった。

HHKBが有名でいいとは聞いていたが3万円とかはさすがに出せない。

秋葉原にいって実際に触ってみてFILCOのを選んだ。

黒軸、赤軸、茶軸とあってなんだろうと思ったが、
触ってみてバネの強さだということはすぐにわかった。

黒はちょっと硬すぎて疲れそうな気がしたので茶を選んだ。

数日使ってみて、まあいいかなとは思うが特に素晴らしいとも思わない。
ややストロークが深すぎる感じがする。

そして、一点、気になるのが、「無変換」キーの位置だ。

「無変換」キーはスペースキーの左側にあり、左手の親指で押すことになるが、
今まで使っていたキーボードより左側にあり、親指をけっこう曲げないと押せない。

いままでの感覚で押すとスペースを押してしまう。

無変換キーはカタカナの語を入力するときに押すからけっこう頻繁に使うキーだ。

まあ、慣れかな.....。


2020/05/02

ラズベリーパイでwindowsのドライブをマウントする

ラズベリーパイで、windowsのドライブをマウントする。
sudo mkdir /mnt/DATA
sudo mount -t cifs //172.16.x.x/DATA/subdir /mnt/DATA -o user=fugafuga,password=hogehoge,iocharset=utf8
つねにマウントするには /etc/fstabに追記
//172.16.x.x/DATA/subdir /mnt/DATA cifs username=fugafuga,password=hogehoge,iocharset=utf8,rw,uid=1000,gid=1000,defaults 0 0
uidおよびgidは id コマンドで確認

2020/04/25

raspberry piでsshやraspi-config起動後ハングアップする

スイッチサイエンスで買い物をしたついでにラズパイ用のLCDディスプレイを買った。

はんだ付けが必要なキットである。

使ってみようと思い、古いラズパイに接続した。

WEBのチュートリアルにpythonのスクリプトサンプルが載っていて、
python3系しかサポートしていないとのことであった。

piでpythonって使ったことがなかったのだが確認すると2系と3系が両方入っていた。

pip3はなかったのでインストールし、サンプルスクリプトを作ろうとviを起動したら、
vi起動後、ハングアップしてしまう。

LCDディスプレイをつないだせいでぶっ壊れたのかと思い、別のpiで試すことにしたら、
それでも同じようにvi起動後にハングアップする。

どういうことだ?

軽くパニックになり、比較的新しいpi3を起動してviを起動したら正常に起動した。


最近わけあって家の無線LANのセグメントを変更した。

pi達には固定IPアドレスを振っているので、すべての固定アドレスを変更し、
ついでにapt update/upgradeをした。

そのせいだろうか?

viはすべてのpiでよく使っていたがこんな風になったことはなかった。


viだけでなく、raspi-configも起動しない。


いつもはsshで操作しているが、コンソールケーブルをつないでアクセスしてみた。

するとviもraspi-configも起動した。

メモリ不足とかか?


あきらめてディスプレイの確認はpi3でした。

翌日、ダメになった2台のpiはSDカードのインストールからやり直すと無事にsshしてviできるようになった。

raspi-configも動く。


なんらかのリソース不足じゃないかと思うのだが、ざっと見メモリとディスクは問題なさそうだった......




2020/03/11

ゲルマニウムラジオ改造

ゲルマニウムラジオを基板にはんだ付けし、ピンソケットを付け、小さいタッパにねじ止めした。

あと、コイルは今までポテトチップのケースに線を巻いた自作のものを使用していが、
かさばるのが嫌で、PA-63Rに置き換えてみたらうまくいったのでそれをケースの中にしまうことにした。

アンテナ、イヤホン(2)、アース、バリコン(2)、コイル(2)

計8本の線をソケットに挿す。

アンテナとアースは部屋に常設しておくことにした。
それぞれ10mくらい。



クリスタルイヤホンで聴こえることは聴こえるが、
ラジオとして実用に耐えうる音量ではない。

今は相撲中継が流れていたが耳をすませて集中していないと何を言っているのかわからない。

選局できるのも2つだけ。おそらくNHK。

もう一局、かすかに聴こえるがAFNか。

先日作ったaitendoのアンプキットにつなげば、実用的な音量にはなる。

あと、ややノイズがあるのだが、イヤホンと並行に0.01μFのコンデンサをつけると軽減する。

以前、「10KΩまたは33KΩの抵抗 + 0.1μFのコンデンサ」をつけると一番よく聴こえた、というエントリーを書いたが、それをやってみたが今回はうまくいかず、

いろいろ試したが0.01μFのコンデンサを付けたときが一番良かった。

ピンソケットに挿す線を忘れないようにメモしておく

ダイオードを上にして

左側が上から
 アンテナ
 バーアンテナ
 バーアンテナ
 バリコン

右側が上から
 イヤホン
 イヤホン
 アース
 バリコン







2020/02/16

djangoとapacheの連携

# httpd -v
Server version: Apache/2.4.37 (centos)
Server built:   Dec 23 2019 20:45:34

/var/www 
で、djangoのプロジェクトを作る。

/var/www/kingyo
を作った。


mod_wsgiをインストール

dnf install python3-mod_wsgi


/usr/lib64/python3.6/site-packages/mod_wsgi/server
にある
mod_wsgi-py36.cpython-36m-x86_64-linux-gnu.so

/etc/httpd/modules
にコピーする。

/etc/httpd/conf.d

django.conf
を作る。

# cat django.conf
LoadModule wsgi_module modules/mod_wsgi-py36.cpython-36m-x86_64-linux-gnu.so

WSGIScriptAlias / /var/www/kingyo/kingyo/wsgi.py
WSGIPythonPath /var/www/kingyo/

<Directory /var/www/kingyo/kingyo>
<Files wsgi.py>
Require all granted
</Files>
</Directory>

/var/www/kingyo

wsgi.py
を作る。


# cat wsgi.py
#!/usr/bin/python
#coding:utf-8

import os
import sys

from django.core.wsgi import get_wsgi_application

sys.path.append("/var/www/kingyo")
sys.path.append("/var/www/kingyo/kingyo")

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "kingyo.settings")


application = get_wsgi_application()


apache再起動

これでapacheのデフォルトサイトがdjangoで作ったサイトになる。

この時、2つほど問題が
1.管理ページにアクセスできない
2.1を解決した後、管理ページのcssが読み込まれない

1.の解決
djangoで作ったプロジェクトフォルダ、dq.sqlsite3、のownerをapacheに変える

2.の解決
settings.py
に以下を追記
STATIC_ROOT = os.path.join(BASE_DIR, "static")

下記を実行
python manage.py collectstatic

/etc/httpd/conf/httpd.conf
にaliasを追加

Alias /static/ /var/www/kingyo/static/

<Directory /var/www/kingyo/static>
    Require all granted
</Directory>

apacheをリスタート

なかなか認証にたどりつけない....
が、この管理サイトというものがすでにpython+djangoで認証しているのである。
これに一般ユーザを追加すればよいのだ。

そしてdjangoはさっきちらっとでてきたが、デフォルトでSQLiteを使っている。

つまり上記の設定により Apache + python + SQLite が動いているのだ。

そこでWSGIという仕組みが使われている。

dbは好きなものに変更でき、postgresqlも使える。









django

pythonのcgiでdbにアクセスできるようになった。
さあ、dbを使ってちょっとしたアプリケーションを作ってみたいが、
何をするにもサービスにログインする仕組みは必要だろう。
以前からログインの仕組みをやりたいと思っていた。
しらべるとdjangoを使った方法が紹介されていたので、
まずdjangoを使ってみる。

centos8, python3が前提


pip intall django

以下は適当な場所にフォルダを作り、そこで実行した。

プロジェクトの作成
# django-admin startprojects myproject

#cd myproject

アプリケーションの作成
#python manage.py startapp myapp

myapp/views.py を編集

from django.shortcuts import render

# Create your views here.

from django.http import HttpResponse

def index(request):
        return HttpResponse("Hello")


myproject/urls.py を編集、
import myapp.views を追加、
最後のところに作成したアプリケーションへのpathを追加

from django.contrib import admin
from django.urls import path
import myapp.views

urlpatterns = [
    path('admin/', admin.site.urls),
    path(r'', myapp.views.index, name='index')
]


myproject/myproject/settigs.py

にアクセスを許可するホスト名を追加する

ALLOWED_HOSTS = ['*']
で、なんでも許可

デフォルトは何も設定されていないので必ずエラーになる。

プロジェクトフォルダにある manage.pyを使って
migrate(設定の反映みたいなもの?)を実行

python manage.py migrate


アプリケーションを実行
xx.xx.xx.xxはサーバのグローバルIPアドレス
アドレスを指定しないと127.0.0.1になり、外部からアクセスできない。

python manage.py runserver xx.xx.xx.xx:8000


fierwall-cmdでtcp8000を開ける。

http://www.example.com:8000
にアクセス

Hello
と表示される。











postgresqlのcsvエクスポートとインポート

usersというテーブルに以下のようなレコードがあった。
                id                |               name
----------------------------------+----------------------------------
 paul                             | Paul McCartney
 pete                             | Pete Townshend
 john2                            | John Ryden
 bob                              | Bod Dylan
 john                             | John Lennon
bobの名前が間違っている。 Bob Dylanに直したい。 Excelあるいはテキスト感覚で直接テーブルを開いて書き換えたいところだが、 それはできない(よね?) csvでエクスポートインポートできるようなのでやってみた。
monqy=# COPY users TO '/var/lib/pgsql/users.csv' WITH CSV DELIMITER ',';
COPY 5
出力先は当然ながら書き込み権限が必要で、書き込み先に権限を与える(postgresの)か、書き込み権限のある場所にエクスポートする。 さっきのBob Dylanを直してインポートしようとしたら
monqy=# COPY users FROM '/var/lib/pgsql/users.csv' with csv;
ERROR:  duplicate key value violates unique constraint "users_pkey"
DETAIL:  Key (id)=(paul                            ) already exists.
CONTEXT:  COPY users, line 1
1件目の登録で already exists のエラーが。 上書きするオプションみたいなものは無いようで、一時的に作ったテーブルにインポートしてそこからupdateするとかいうことが必要らしい。 しょうがないので updateで更新。
monqy=# UPDATE users set name = 'Bob Dylan' where id = 'bob';
UPDATE 1
monqy=# select id,name from users;
                id                |               name
----------------------------------+----------------------------------
 paul                             | Paul McCartney
 pete                             | Pete Townshend
 john2                            | John Ryden
 john                             | John Lennon
 bob                              | Bob Dylan
(5 rows)
更新後のテーブルを表示すると更新したレコードが最後になっている。 削除・登録になっているのか?

psycopg2 検索cgi

DBといえば検索だよね。

私は何かを探す場合はほとんどシーケンシャル処理で正規表現でさがす。
あつかうデータがいろいろでフォーマットも不明な場合が多いからだ。
でもDBがあるなら、キーで検索すれば瞬時に見つかる。


検索フォーム
<!DOCTYPE html>
<html>
<head>
<title>dbq</title>
</head>
<body>
<form action="/cgi-bin/dbq.py" method="POST">
  <input type="text" name="text" value="id" />
  <input type="submit" name="submit" />
</form>
</body>
</html>
検索cgi

dbq.py


#!/usr/bin/python

import cgi
import psycopg2
import psycopg2.extras
import os

path = "localhost"
port = "5432"
dbname = "monqy"
user = "postgres"
password = "hogehoge"

pg_params = "host={} port={} dbname={} user={} password={}"
pg_params = pg_params.format(path,port,dbname,user,password)

connection = psycopg2.connect(pg_params)
cur = connection.cursor(cursor_factory=psycopg2.extras.DictCursor)

html_body = """
<!DOCTYPE html>
<html>
<head>
<title>your query</title>
<style>
h1 {
font-size: 3em;
}
</style>
</head>
<body>
<h1>%s</h1>
</body>
</html>
"""

form = cgi.FieldStorage()
qtext = form.getvalue('text','')

cur.execute('SELECT * FROM Users WHERE id = %s', (qtext,))

results = cur.fetchone()

if results is None:
        text = "not found"
else:
        text = results['name']

cur.close
connection.close

print(html_body % (text))

テーブル users から、idというフィールドが検索文字列と一致するレコードを探し、
一致しなければ「not found」、一致すればそのレコードの name フィールドを表示する。

単純な動作なのでフォームや実行結果を表示するまでもないでしょう。

このテーブルはid がkeyであり重複するレコードはないので fetchoneを使っている。

「レコードが見つからない場合」をどう書くのか、少し探した。
見つからない場合に results をそのまま表示すると「None」となることはわかったのだが、
results eq None
とか
results == "None"
ではダメで、
正しくは
results is None
であった。


2020/02/15

python3のcgiでpostgresqlのデータを参照する @CentOS8

pythonからpostgresqlを使うためのライブラリとして psycopg2というものがよく使われているようである。 

エラーが出ては足りないものをインストールした結果、 以下が必要であった。
dnf install postgresql-devel
dnf install gcc
dnf install python3-devel
pip install psycopg2

pythonでcgiが動くことはすでに確認済み。 
OSはCentOS8、pythonは3.6.8である。 

以下がpostgresqlのDBに登録されている内容を表示するcgiの例である。 

ローカル(cgiを実行しているサーバ)にある、 
monqy というデータベースの、 usersというテーブルに登録されているレコードの、 nameという項目の内容を、すべて表示する。
   
#!/usr/bin/python

import cgi
import psycopg2
import psycopg2.extras
import os

path = "localhost"
port = "5432"
dbname = "monqy"
user = "postgres"
password = "hogehoge"

pg_params = "host={} port={} dbname={} user={} password={}"
pg_params = pg_params.format(path,port,dbname,user,password)

sql = """select * from users"""

print('Content-Type: text/html; charset=utf-8')
print('')
print('')

connection = psycopg2.connect(pg_params)
cur = connection.cursor(cursor_factory=psycopg2.extras.DictCursor)
cur.execute(sql)

results = cur.fetchall()
for row in results:
        print(row['name'])
        print("
") cur.close connection.close print("")
この例ではDBの内容を辞書形式で取得することによって、項目名で値を取り出している。 

せっかくDBを使っているのに名前を使わないなんてわざわざライブラリを使う意味がないだろうから辞書形式で取得するのは必須だろう。

postgresqlをインストールしテーブルにデータを登録するまで(postgresql on CentOS8)

インストール

# dnf install postgresql

サーバーをインストール

# dnf install postgresql-server


初期化

# postgresql-setup initdb


設定ファイル変更
# cd /var/lib/pgsql/data/

デフォルト設定をバックアップ
# cp postgresql.conf postgresql.conf.org

# vi /var/lib/pgsql/data/postgresql.conf

最後の行に1行追加

  ....
  # CUSTOMIZED OPTIONS
  #------------------------------------------------------------------------------
  # Add settings for extensions here
  listen_addresses = '*'


もう一個設定ファイル変更

# cd ../data
# pwd
/var/lib/pgsql/data

# cp pg_hba.conf pg_hba.conf.org
# vi pg_hba.conf

以下のように変更
# cat pg_hba.conf

  # PostgreSQL Client Authentication Configuration File
  # ===================================================
 local   all     all                     trust
  host    all     all     127.0.0.1/32    trust
  host    all     all     ::1/128         trust




postgresqlを再起動

# systemctl restart postgresql

active(running) になる。

# systemctl status postgresql

● postgresql.service - PostgreSQL database server

   Loaded: loaded (/usr/lib/systemd/system/postgresql.service; disabled; vendor preset: disabled)

   Active: active (running) since Sat 2020-02-15 13:58:56 JST; 1s ago

  Process: 14332 ExecStartPre=/usr/libexec/postgresql-check-db-dir postgresql (code=exited, status=0/SUCCESS)
(以下略)


接続

# psql -U postgres -h 127.0.0.1 -w
psql (10.6)
Type "help" for help.

postgres=#

^Zで抜ける


パスワード変更

# psql -U postgres -c "ALTER ROLE postgres WITH PASSWORD 'hogehoge'"
ALTER ROLE

データベース作成


#psql -U postgres -W -c "CREATE DATABASE my_db";
Password for user postgres:

CREATE DATABASE

データベースを指定して接続

psql -U postgres my_db
my_db=#

Users というテーブルを作る

my_db=# create table Users (
my_db(#   userid         char(032)     primary key,
my_db(#   fullname       char(100),
my_db(#   phonenum       char(11),
my_db(#   postal         char(7),
my_db(#   created        timestamp,
my_db(# active boolean);
CREATE TABLE

my_db=#

確認

my_db=# \d
         List of relations
 Schema | Name  | Type  |  Owner
--------+-------+-------+----------
 public | users | table | postgres

(1 row)



テーブル定義を表示



my_db=# \d Users

                          Table "public.users"
  Column  |            Type             | Collation | Nullable | Default
----------+-----------------------------+-----------+----------+---------
 userid   | character(32)               |           | not null |
 fullname | character(100)              |           |          |
 phonenum | character(11)               |           |          |
 postal   | character(7)                |           |          |
 created  | timestamp without time zone |           |          |
 active   | boolean                     |           |          |
Indexes:
    "users_pkey" PRIMARY KEY, btree (userid)


テーブルにレコードを追加

my_db=# INSERT INTO Users(userid,fullname,created) VALUES ('john','John Lennon', current_timestamp);

INSERT 0 1

my_db=# INSERT INTO Users(userid,fullname,created) VALUES ('paul','Paul McCartney', current_timestamp);

INSERT 0 1


テーブルのレコードを表示



my_db=# select * from Users;

   userid   |   fullname      | phonenum | postal |     created                | active
------------+-----------------+----------+--------+----------------------------+--------
 John       | John Lennon     |          |        | 2020-02-15 14:23:36.832459 |
 monqy      | Paul McCartney  |          |        | 2020-02-15 14:23:53.176765 |
(2 rows)


※実際は項目長の長さだけ表示されるので折り返して汚くなったが上記は見やすく整形してある。



とりあえず一つのテーブルが作成できた。

今度はこのDBの内容をCGIで表示してみる。
そして登録、修正、削除ができるようにする。