sftpをプロキシ(squid)または踏み台(ポートフォワーディング)経由で使用できるようにする

はじめに

やりたかったことはタイトル通りなのですが。

登場人物は以下
・作業用PC
・中継サーバー(踏み台 or プロキシ)
・開発サーバー

今回は中継サーバーと開発サーバーはcentos6.5で、作業PCはMacで確認。

アクセス制限は以下
・開発サーバーへのSSHでのアクセスは中継サーバーからしか接続できない。
・中継サーバーへは作業PCから接続できる。

作業するのがプログラマ以外なのでFTPクライアントソフトとかで接続できるようにしたい。というのがやりたかったことです。

調べてみて簡単に実装できそうな方法が2つ。
SSHポートフォワーディングを使う
・中継サーバーをプロキシサーバーにする
squidをいれてプロキシサーバーとする。作業者は中継サーバをプロキシに設定する形

これから先、サーバー名は以下とする
中継サーバー = relay
開発サーバー = dev

SSHポートフォワーディングを使う方法

ポートフォワーディング

$ ssh -N -L 2022:devのIP:devのポート relayのユーザ名@relayのIP
# Ctrl + cで終了
# 2022は適当なポート番号(作業用PCで空いてる番号)

SFTPで接続

$ sftp -oPort=2022 devのユーザ名@localhost

SFTP接続はターミナルから接続する例なのでコマンドだが、FileZilla等のFTPソフトを使ってる場合も上記の情報を設定すれば接続できる。

中継サーバーをプロキシサーバーにする

こっちの方がsquidを入れる必要があるので少し手間。
あと、SSHポートをプロキシとして解放するのでIP制限等をしない場合は、SSHのポート番号を変更した方がよさそう。今回は60022とかで話を進める。開発サーバー(接続先)のsshd_configでsshのポート番号を変更できるのでこちらは設定済であるとします。

squidのセットアップ

squidのインストール

$ yum install -y squid

/etc/squid/squid.confの編集

以下の項目をコメントアウト
#http_access deny all

http://qiita.com/pcnikki/items/404329f9ad9cb6e235d4
ここら辺とか参考にして最低限の設定はしておいた方がよさそう。
今回はsquidの公開ポートはデフォルトの3128で。

Basic認証設定

/etc/squid/squid.confの編集

# 以下を追加
auth_param basic program /usr/lib64/squid/ncsa_auth /etc/squid/passwd
acl password proxy_auth REQUIRED
http_access allow password

Basic認証用のユーザーを追加(更新、削除)

$ htpasswd -c /etc/squid/passwd user # 作成
$ htpasswd -b /etc/squid/passwd user pass # 追加
$ htpasswd -D /etc/squid/passwd user # 削除
SSHのポート番号をsquidで使えるようにする

/etc/squid/squid.confの編集

・・・
acl SSL_ports port 443
acl SSH_ports port 60022 # ★ココを追加
acl Safe_ports port 60022 # ★ココを追加
・・・

# ★ ココを書き換えてSSHもConnectメソッドを使えるように
# http_access deny CONNECT !SSL_ports
http_access deny CONNECT !SSL_ports !SSH_ports
・・・

squid起動

$ service squid start
SFTPでつなぐ
$ sftp -o "ProxyCommand connect -H basic認証のユーザ名@relayのIP:3128 %h %p" devのユーザー名@devのIP

とりあえず動いた。以上です

参考url
SquidでSSHをプロキシする設定。 - それマグで!
5分で作るPROXYサーバー - Qiita

【squid】プロキシにパスワード認証を設定する

次回のために作業メモ
squidのプロキシにパスワード認証(Basic認証)追加

ユーザー作成

# 新規作成
$ htpasswd -c /etc/squid/passwd user

# ユーザー追加
$ htpasswd -b /etc/squid/passwd user2 pass

# ユーザー削除
$ htpasswd -D /etc/squid/passwd user2

/etc/squid/squid.conf

# 追加
auth_param basic program /usr/lib64/squid/ncsa_auth /etc/squid/passwd
acl password proxy_auth REQUIRED
http_access allow password

再起動で設定反映

$ service squid restart

以上です

【android】Fragment上のListViewに置かれたボタンのonClickを処理する

はじめに

今回やりたかったのは、ListViewのセル上に削除ボタンとかをおいてクリックされたらListViewから削除するようなこと。
ListViewを作るのがActivityとFragmentとでやり方が違うみたい。

Activityの場合は以下みたいな形でボタンのイベントが取得できる。

xml

<Button ・・・
  android:id="@+id/myButton"
  android:onClick="myFunc"
 />

Adapter

public View getView(final int position, View convertView, ViewGroup parent) {
    ・・・
    Button myButton = (Button)convertView.findViewById(R.id.myButton);
    myButton.setTag(position);
    ・・・
}

Activity

public void myFunc(View view) {
    // ListView上のボタンがおされたら呼ばれる
    // view.getTag()でどの行が押されたかわかる
}

けど、Fragmentを使ってる場合はFragmentを呼び出してるActivityで実装されているmyFuncが呼ばれるためやりたいこととは違った。

Fragmentの場合

xmlのButtonにonClick属性はセットしない。
やり方わからなかったので、アダプターにインターフェースを定義して呼び出し元(Fragment)にメソッドを実装させる形で実装してみた。

xml

<Button ・・・
  android:id="@+id/myButton"
 />

Adapter

public class MyAdapter extends ArrayAdapter<MyData> {

    private MyAdapterListener listener;

    public interface MyAdapterListener {
        void myFunc(Object position);
    }

    public void setListener(MyAdapterListener listener) {
        this.listener = listener;
    }

    ・・・

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        ・・・
        Button myButton = (Button)convertView.findViewById(R.id.myButton);
        myButton.setTag(position);
        myButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                listener.myFunc(v.getTag());
            }
        });
        return convertView;
    }
}

Fragment

public class MyFragment extends Fragment implements MyAdapter.MyAdapterListener {
    ・・・
    @Override
    public void onCreate(Bundle savedInstanceState) {
        ・・・

        // アダプターで用意したsetListnerメソッドで関連づける
        mAdapter = new MyAdapter(getContext(), 0, ・・・);
        mAdapter.setListener(this);
    }

    public void myFunc(Object position) {
        // 呼ばれた!!
    }
}

もっといいやり方ありそうだけど。。以上です

【android】ListViewひな型

試したりするときにとりあえずListView作ることが多いのでコピペ用にメモ

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private ArrayList<Item> mItems;
    private ItemAdapter mAdapter;
    private ListView mListView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mItems = new ArrayList<>();
        mItems.clear();
        mAdapter = new ItemAdapter(this, 0, mItems);

        ListView listView = (ListView)findViewById(R.id.list_view);
        listView.setAdapter(mAdapter);

        setData();
    }

    private void setData() {
        for (int i = 0; i < 10; i++) {
            Item item = new Item();
            item.setTitle("タイトル " + String.valueOf(i+1));
            mItems.add(item);
        }
    }
}

class Item {
    private String title;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}

class ItemAdapter extends ArrayAdapter<Item> {
    private LayoutInflater layoutInflater;

    public ItemAdapter(Context c, int id, ArrayList<Item> data) {
        super(c, id, data);
        this.layoutInflater = (LayoutInflater)c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = layoutInflater.inflate(R.layout.list_item,parent,false);
        }
        Item data = getItem(position);

        TextView title = (TextView)convertView.findViewById(R.id.title);
        title.setText(data.getTitle());

        return convertView;
    }
}

main_activity.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:id="@+id/activity_main"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:paddingBottom="@dimen/activity_vertical_margin"
                android:paddingLeft="@dimen/activity_horizontal_margin"
                android:paddingRight="@dimen/activity_horizontal_margin"
                android:paddingTop="@dimen/activity_vertical_margin">

  <ListView
      android:id="@+id/list_view"
      android:layout_width="match_parent"
      android:layout_height="match_parent"></ListView>
</RelativeLayout>

list_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical" android:layout_width="match_parent"
              android:layout_height="match_parent">

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingLeft="5sp"
        android:paddingTop="25sp"
        android:paddingBottom="15sp"
        android:textSize="24sp"/>
</LinearLayout>                                                                                                                                                                                                    
||<                                                                                                                                                                                                                

【android】ViewPagerでタブ毎に戻れる画面を実装する方法

はじめに

今回やりたかったのはこんな画面。
f:id:yoppy0066:20170131000025g:plain:w250

なんでもiOSと比較するのはあれだけど、iOSだとUiNavigationControllerを持つUITabbarControllerみたいなイメージ。
今回はタブは表示していないけどViewPagerをタブ表示するサンプルはたくさんあるので問題なかった。
戻るボタンでの戻り先をタブごとに保持しているようなイメージ。

クラス構成としてはこんなイメージ。
Activityは1つでタブごとにRootFragmentを持たせて、実際に画面に表示するフラグメントを操作するイメージ(ここではFirstFragmentとSecondFragment)。

f:id:yoppy0066:20170131000304p:plain

実装

MainActivityの実装

ここでやることはViewPagerとRootFragmentを紐づける処理。
あとは戻るボタンされた時に選択中のタブのバックスタックから1つ取り出して戻す処理。
戻るボタンされたときに選択中のタブがわかるようにタブ選択されるたびに選択中のタブ番号を保持する。

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private ViewPager mPager;
    private String[] mTitles = {"タブ1", "タブ2"};

    // 選択中のタブ番号を保持
    private int selected;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_main);
        setTitle("タブ1");
	mPager = (ViewPager)findViewById(R.id.viewpager);

        mPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
		selected = position;
		setTitle(mTitles[position]);
            }
            ・・・
        });

        FragmentPagerAdapter adapter = new FragmentPagerAdapter(getSupportFragmentManager()) {
            @Override
            public Fragment getItem(int position) {
                return RootFragment.newInstance(mTags[position]);
            }

            @Override
            public int getCount() {
                return 2;
            }
            ・・・
        };
        mPager.setAdapter(adapter);
    }

    // 戻るボタン
    @Override
    public void onBackPressed() {

        // 選択中のタブのRootFragmentにバックスタックがあれば戻る処理
        FragmentManager fm = getSupportFragmentManager();
        List<Fragment> fragments = fm.getFragments();
        Fragment fragment = fragments.get(selected);
        FragmentManager fragmentManager = fragment.getChildFragmentManager();
        if (0 < fragmentManager.getBackStackEntryCount()) {
            fragmentManager.popBackStack();
        }
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools"
                android:id="@+id/activity_main"
                ・・・
                />

  <android.support.v4.view.ViewPager
      android:id="@+id/viewpager"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:layout_weight="1"
      android:background="@android:color/white"
      />

</RelativeLayout>
RootFragmentの実装

ここではFirstFragmentを表示するだけ。それならRootFragmentなんて不要と思って最初実装していた。
けど、ViewPagerの中のFragmentをreplaceしても中身がレイアウトの表示が変わらない問題に直面。で

http://www.pineappslab.com/post/fragments-viewpager/
ここの記事を発見。ここの通りにやらせていただきました。

RootFragment.java

public class RootFragment extends Fragment {

    public static RootFragment newInstance() {
        RootFragment fragment = new RootFragment();
        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_root, container, false);
        FragmentTransaction fragmentTransaction = getChildFragmentManager().beginTransaction();
        fragmentTransaction.replace(R.id.root_frame, FirstFragment.newInstance());
        fragmentTransaction.commit();
        return view;
    }
}

fragment_root.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/root_frame"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</FrameLayout>
FirstFragmentの実装

FirstFragment.java

public class FirstFragment extends Fragment {

    public static FirstFragment newInstance(String tag) {
        FirstFragment fragment = new FirstFragment();
            Bundle args = new Bundle();
            return fragment;
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.fragment_first, container, false);
            Button button = (Button) view.findViewById(R.id.button);
            button.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {
                FragmentTransaction fragmentTransaction = getParentFragment().getChildFragmentManager().beginTransaction();
                fragmentTransaction.replace(R.id.root_frame, SecondFragment.newInstance());
                fragmentTransaction.addToBackStack();
                fragmentTransaction.commit();
            }
        });
         return view;
    }
}

fragment_first.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

  <TextView
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_marginBottom="10sp"
      android:text="FirstFragment"
      android:textSize="30sp"/>

  <Button android:id="@+id/button"
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          android:background="#ffff00"
          android:text="次へ"
          android:textSize="18sp"/>

</LinearLayout>

SecondFragmentは画面表示するだけだからいいかな。。。

おわりに

よくわからないで調べながらやってたからこの画面実装するポイントは他にもけっこうあるはずなんだけど出てこない。。。
もうちょっと内容を分割して整理した方が良さそう。以上です

はてなブログ(はてな記法)で画像を横並びにする方法メモ

なにげに時間かかったのでメモしておく。
ちなみに自分は、はてな記法モードっていうのを使ってる。

手順は以下だった
・アップロードした画像のURLを取得
・tableタグとimgタグ使って整形


やりたかった画面




アップロードした画像のURLを取得

アップロードした画像ははてなフォトにあるのでここから取得できる。

1. マイフォトを開く
f:id:yoppy0066:20170127112543p:plain

2.Hatena Blogフォルダを開く
f:id:yoppy0066:20170127112552p:plain

3. フォルダ編集を開く
f:id:yoppy0066:20170127112601p:plain

4. 画像を選択してブログを貼り付けるをクリックするとurlを取得できる
f:id:yoppy0066:20170127112607p:plain

tableタグとimgタグ使って整形

あとはtableタグとimgでスタイルシートも使えるのでこんなかんじで。

<table style="border-style: none;" border="1"><tr>
  <td style="border-style: none;">
    <img src="画像のURL" height="150">
  </td>
  <td style="border-style: none;">
    <img src="画像のURL" height="150" />
  </td>
</table>

以上です